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.
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*
}
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?
I have a problem, I have to save into a txt file a lot of data, the problem is that I receive them every 100ns from a serial port.
How can i do? I tried to put data on a RichTextBox but I receive a thread error.
Sorry for my English.
I'm using Windows 7 64bit
private: void FindPorts(void)
{
array<Object^>^ ListaPorte = SerialPort::GetPortNames();
this->cmbPort->Items->AddRange(ListaPorte);
}
private: System::Void btnOpenPort_Click(System::Object^ sender, System::EventArgs^ e)
{
if(this->cmbPort->Text==String::Empty || this->cmbBaudRate->Text==String::Empty)
this->rtbLog->Text="cmbError";
else
{
try{
if(!this->serialPort->IsOpen)
{
this->serialPort->PortName = this->cmbPort->Text;
this->serialPort->BaudRate = Int32::Parse(this->cmbBaudRate->Text);
this->serialPort->Open();
this->rtbLog->Text+="Porta aperta\r\n";
this->btnOpenPort->Text="Close Port";
}
else
{
this->serialPort->Close();
this->rtbLog->Text+="Porta chiusa\r\n";
this->btnOpenPort->Text="Open Port";
}
}
catch(UnauthorizedAccessException^)
{
this->rtbLog->Text+="UnauthorizedAccessException\r\n";
}
}
}
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e)
{
if(!lettura)
{
lettura = true;
this->button1->Text="sto leggendo";
}
else
if(lettura)
{
lettura=false;
//rtbLog->Text += letturaBuffer;
this->button1->Text="sono ferma";
}
//serialPort->
}
private: System::Void serialPort_DataReceived(System::Object^ sender, System::IO::Ports::SerialDataReceivedEventArgs^ e)
{
if(lettura){
//letturaBuffer += serialPort->ReadExisting() + "\r\n";
rtbLog->Text += serialPort->ReadExisting() + "\r\n";
}
}
With a normal OS you wont be able to hold the pace if the data always comes at 100ns interval. 100ns interval is 10MHz so you should look for a realtime OS's that can deal with that speed. To display it in realtime you need lightweight components, so actually you should benchmark that RichTextBox and check if it can handle 10MHz.
Another problem you might have is the interrupt latency, if it takes 1us to get it, you already missed 10 data transfers.
It's unlikely you're getting data every 100 ns: The maximum speed for a standard serial port is 115,200 bits per second, which is 11,520 bytes per second, maximum. Even if your event is getting signaled for every byte received (also unlikely), that's every 87 microseconds, not every 100 nanoseconds.
The reason you're getting a thread error is because the serialPort_DataReceived method is being called on a threadpool thread, and UI components can only be accessed from the UI thread. Let's fix that, then worry about how often you want to do this, and whether it's possible in a non-RTOS.
It looks from your code like you're using WinForms, not WPF. Give this a try:
void AddAString(String^ s)
{
this->rtbLog->Text += (s + Environment::NewLine);
}
void serialPort_DataReceived(Object^ sender, SerialDataReceivedEventArgs^ e)
{
if(lettura)
{
this->BeginInvoke(
gcnew Action<String^>(this, MyClass::AddAString),
serialPort->ReadExisting());
}
}
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.