I like to use the Sleep() function in a Windows Forms project, but the Sleep() is executed before anything else. I read that i should flush using fflush(), but i don't know what to flush. Can someone help me?
The code:
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
this->label1->Visible= false;
this->button1->Visible= false;
r = (float)rand()/(float)RAND_MAX;
r = r*100000;
i = r;
r = r - i;
String^ strR = "" + r;
this->label2->Text = strR;
if(r >= 0.5)
{
this->pictureBox1->Visible= true;
this->pictureBox1->BackColor = System::Drawing::Color::Blue;
}
else
{
this->pictureBox1->Visible= true;
this->pictureBox1->BackColor = System::Drawing::Color::Red;
}
Sleep(500);
}
The call to Sleep() is blocking your main (UI) thread, which prevents the message pump from updating your controls.
In this case, it doesn't appear that the call to Sleep really serves a purpose, other than blocking your UI - if you want to prevent the button from being pressed again, a better option would be to disable it, then use a timer (System::Windows::Forms::Timer) with a 500 ms interval to re-enable the button.
By using a timer, you won't block the UI thread, which allows your controls to stay active, but you still prevent the user from pressing the button.
Related
I want to implement a GUI that receives messages from an external device. The "advancedReceiveExample" is waiting for messages. Once it has received one, it does stuff with it, saves it and terminates.
I want to make my function wait for new messages after receiving one as long as the button is toggled.
I have tried this so far:
void MainWindow::on_pushButton_clicked()
{
if (ui.pushButton->isChecked()) {
ui.pushButton->setText("Stop Receiving");
ui.label_3->setText("Receiving...");
advancedReceiveExample(ui.comboBox->currentIndex() + 1);
}
else
{
ui.pushButton->setText("Start Receiving");
ui.label_3->setText("Not Receiving");
}
}
This works perfectly fine but as mentioned above it only receives one message. If I do that:
void MainWindow::on_pushButton_clicked()
{
if (ui.pushButton->isChecked()) {
ui.pushButton->setText("Stop Receiving");
ui.label_3->setText("Receiving...");
while (1)
{
advancedReceiveExample(ui.comboBox->currentIndex() + 1);
}
}
else
{
ui.pushButton->setText("Start Receiving");
ui.label_3->setText("Not Receiving");
}
}
it blocks the function because the state of the button can only be change after the function "on_pushButton_clicked()" has terminated.
Visual Studio 2019
C/C++
EDIT: Okay, I have understood the problem of blocking the thread. Multithreading might be the right option but I am very unexperienced regarding this topic. The <QThread> could be possible. How would you use it?
Do you have suggestions which other library could be used?
Note QT is event-based. If you keep your computer busy inside some function without returning to the main loop frequently, your GUI will freeze.
What you need to do is slice your action that you want to do into small bits that can repeatedly return to the main loop in order to keep the GUI responsive. (Another method yould be to swap out your action into a separate thread and handle it in parallel, killing the thread when the button is released)
Probably the simplest method to do what you want is with timers that you arm in the PushButton::clicked slot, and then check in the timer event whether the button is still pressed, and, if yes, do a bit of your action, save state and re-arm the timer to have you return.
Something along the lines of the following pseudo code should work and execute what you want to do in slices every 10ms:
MainWindow::onPushButtonClicked () {
// do the action, or, alternatively, start a
// parallel thread that does it
do_a_bit_of_action();
// sets up a timer to call onTimer after 10ms
QTimer::singleShot (10, this, SLOT(onTimer()));
}
MainWindow::onTimer () {
// check if button is still held down
if (pushButton.down) {
// re-arm timer
Timer::singleShot (10, this, SLOT(onTimer()));
// do some more action bits
do_a_bit_of_action();
}
else {
// kill optional background thread here
}
}
You can try it with:
while(ui.pushButton->isChecked()){
*your function*
}
I have a wxwindows application and in the onclick event of a button, I have a very long process, for example I have something such as this:
for(int i=1;i<100;i++)
{
sleep(1000);
gaugeProgress->SetValue(i);
*textOutput<<i;
}
Running this code, stops UI to be responsive. I add
Refresh();
Update();
just after
*textOutput<<i;
but it did not work.
is there any way that I can pump the events?
I am working on Windows using VS 20102
In those cases I use wxYield() like this:
for(int i = 1; i < 100; i++)
{
// sleep() freezes the program making it unresponsible.
// sleep(1000);
gaugeProgress->SetValue(i);
*textOutput << i;
// wxYield stops this function execution
// to process all the rest of stocked events
// including the paint event and resumes immediately.
wxYield();
}
This stops the current process and lets the application to process the message stack like the paint event.
But I think that the proper way to do this should be using threads.
You can add a wxTimer member in your wxwindows, start it in the window constructor, as such:
m_timer.Start(1000);
then capture the timer event with a function, for example:
void mywindow::OnTimer(wxTimerEvent& event)
{
Refresh();
Update();
}
Make sure you connect the event to the wxTimer member.
So I made a GUI in C++ which calls a child process every time a button is clicked twice. The StandardOutput is redirected, doesn't use ShellExecute.
I made a simple dummy process to test it, let's say dummy.exe, which basically just do this :
void() {
printf("0");
}
And that's all. The process will exit itself after 0 is plotted.
The process is started when a button is clicked, which does this :
private: System::Void bt_getData_Click(System::Object^ sender, System::EventArgs^ e) {
if (bt_getData->Text == "Get Data") {
proc->Start();
bt_getData->Text = "Stop";
}
else if (bt_getData->Text == "Stop") {
bt_getData->Text = "Get Data";
}
}
Then it will read the output using the OutputDataReceived EventHandler.
The problem is when I clicked the button again, the process will be Restarted, but the GUI can't read the new Output.
Case 1 : I cancelled the output read in the OutputDataReceived EventHandler then restart the process, but the next restarted process output can't be read.
private: System::Void outputData(System::Object^ sender, System::Diagnostics::DataReceivedEventArgs^ e) {
x0 = xt;
xt += 1;
if (xt*x_scale > pb_Graph->Width) {
x0 = 0;
xt = 0;
imgTemp = gcnew Bitmap(pb_Graph->Image, 460, 460);
gpcGraph->Clear(Color::Transparent);
}
y0 = yt;
yt = Convert::ToInt16(e->Data);
ret_index++;
if (ret_index > 2047) ret_index = 0;
gpcGraph->DrawLine(greenPen,(float)x0*x_scale,pb_Graph->Height - (float)y0/y_scale - y_null,(float)xt*x_scale,pb_Graph->Height - (float)yt/y_scale - y_null);
pb_Graph->Refresh();
}
After three times restart, this error shows :
An unhandled exception of type 'System.InvalidOperationException' occurred in System.dll
Additional information: An async read operation has already been started on the stream.
Case 2 : I didn't cancel the output read. The same error with case 1 shows, but it is still understandable to me.
Case 3 : I didn't redo the BeginOutputReadLine() when restarting. The error doesn't shown, but the restarted process output can't be read.
My actual goal is to restart the process periodically using a 1 mS timer, so I tested the restart process first using button. But it seems that the newly generated output can't be read.
Any help would be appreciated :)
Okay... I've managed to prevent the errors using different method, which is to call
process->StandardOutput->ReadLine();
This way the async stream reading won't happen.
I am addind a python editor to a C++ application. In order to be able to stop the python process when the user presses ctrl-c, I have 2 threads:
1- the first one runs the python string;
2- the second one polls the keyboard to check for a keyboard interupt.
I use microsoft concurrency library to create the threads:
auto task1 = Concurrency::make_task([&]
{
task1_id = GetCurrentThreadId();
Run(Script);
bFinish = true;
});
auto task2 = Concurrency::make_task([&]
{
while(true)
{
if ((GetKeyState (0x43) < 0 && (GetKeyState (VK_CONTROL) < 0)) && task1_id != 0)
{
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
int t = PyThreadState_SetAsyncExc(task1_id,PyExc_KeyboardInterrupt);
PyGILState_Release(gstate);
break;
}
else if(bFinish)
{
break;
}
else
{
Sleep(10);
}
}
});
m_tasks.run(task2);
Concurrency::task_group_status test = m_tasks.run_and_wait(task1);
This usually works pretty well. If a ctrl-c event happens, a keyboardinterrupt event will happend on the 'script thread' and everything will stop.
A problem happends when I use matplotlib to create some figure, and send a ctrl-c event while the figures are still open. The event will stop everything like before, but the tkinter windows will not be destroyed and clicking on them will give a run time error:
Fatal Python error: PyEval_RestoreThread: NULL tstate
Is there any way I could force those windows to be properly destroyed by the keybordinterrupt exception?
This is a Visual Studio Express C++ Windows Forms application.
I want to play a WAV file strFileName.WAV in a WAV play wavPlayer every 10 seconds from the time I press the "Start" button until such time as I press the "Stop" button.
When each 10 second interval is up, TimerEventProcessor plays the WAV file.
The problem is that I have to press "Stop" twice to get it to work. The first press of "Stop" seems to be ignored.
Why is btnStop_Click not executing the first time I press "Stop"?
private: System::Void bntStart_Click(System::Object^ sender, System::EventArgs^ e) {
if (String::IsNullOrEmpty(strFileName)) {
lblRunning->Text = L"Cannot Start Until File Is Loaded";
}
else {
lblRunning->Text = L"Running";
myTimer->Interval = iIntervalSeconds * 1000;
myTimer->Tick += gcnew EventHandler( TimerEventProcessor );
myTimer->Enabled = true;
while (lblRunning->Text == L"Running") {
Application::DoEvents();
}
}
}
private: System::Void btnStop_Click(System::Object^ sender, System::EventArgs^ e) {
lblRunning->Text = L"Stopped";
myTimer->Enabled = false;
wavPlayer->Stop();
}
Get rid of the
while (lblRunning->Text == L"Running") {
Application::DoEvents();
}
loop. When you return from bntStart_Click the form will return to dispatching messages and the timer will tick as expected. There is no need for you to create a manual Application::DoEvents() loop, which is probably the cause of your problem.
By calling Application::DoEvents(); in a loop you are creating a pooling loop for window messages. Without any sleep call this loop cause 100% CPU usage. By letting your bntStart_Click return the WinForms runtime will sleep your process until a message is ready - resulting in very low CPU usage.
This high CPU usage is probably making your application unresponsive which is why it seems that clicks are not being processed.