QCloseEvent wait for a function to terminate
-
I've wrote a small program just to check serial ports to which COM-name they are mapped. In fact i connect a Keithley Nanovoltmeter to a port, select in the gui the port name by port name until the device answers. When it answers the function keeps reading out the current voltage in a loop. But when I close the app by clicking on the X-button, this function will not be stopped and the program does not terminate correctly. So I tried to use the function closeEvent to set the stop flag and to wait until the running function terminates. But the program keeps hanging in this closeEvent function. How can I implement this?
#include "comportchecker.h" #include "ui_comportchecker.h" #include <QtSerialPort/QSerialPortInfo> #include <QElapsedTimer> #include <QTimer> #include <QCloseEvent> COMPortChecker::COMPortChecker(QWidget *parent) : QMainWindow(parent) , ui(new Ui::COMPortChecker) { ui->setupUi(this); const auto availablePorts = QSerialPortInfo::availablePorts(); for (const QSerialPortInfo &info : availablePorts) ui->serialCombo->addItem(info.portName()); ui->serialCombo->model()->sort(0); serial = new QSerialPort(this); ui->stoppButton->setEnabled(false); } COMPortChecker::~COMPortChecker() { delete ui; } void COMPortChecker::closeEvent(QCloseEvent *e) { if(!stop) { stop = true; do { QEventLoop loop; QTimer::singleShot(50, &loop, SLOT(quit())); loop.exec(); } while (stop); } e->accept(); } void COMPortChecker::on_startButton_clicked() { stop = false; bool found = true; if(serial->isOpen()) serial->close(); serial -> setBaudRate(9600); serial -> setDataBits(QSerialPort::Data8); serial -> setParity(QSerialPort::NoParity); serial -> setStopBits(QSerialPort::OneStop); serial -> setFlowControl(QSerialPort::NoFlowControl); serial -> setPortName(ui->serialCombo->currentText()); if(!serial -> open(QIODevice::ReadWrite)) { return; ui->statusBar->showMessage(QString("Could not open %1").arg(serial->portName()), 20000); } ui->startButton->setEnabled(false); ui->stoppButton->setEnabled(true); QStringList cmds; cmds << "*RST"; cmds << "*CLS"; cmds << ":INIT:CONT OFF;:ABORT"; cmds << ":SENS:FUNC 'VOLT:DC'"; cmds << ":SENS:VOLT:DC:RANG:AUTO ON"; cmds << ":SENS:VOLT:DC:NPLC 1"; // 1 = Medium, 5 = Slow cmds << ":SYST:AZER:STAT ON"; cmds << ":INIT"; foreach (QString cmd, cmds) { QByteArray arr = cmd.append("\r").toLatin1(); serial->write(arr); QEventLoop loop; QTimer::singleShot(50, &loop, SLOT(quit())); loop.exec(); } QEventLoop loop; QTimer::singleShot(1000, &loop, SLOT(quit())); loop.exec(); serial->write(":SYST:VERS?\r"); //serial->write(":FETC?\r"); QString answer = ""; int counter = 0; do { serial->waitForReadyRead(20); answer.append(serial->readAll()); counter++; } while (!answer.contains("\r") && counter < 50); if(counter == 50) { stop = true; found = false; ui->statusBar->showMessage(QString("No Keithley found at %1").arg(serial->portName()), 10000); } else { ui->statusBar->showMessage(QString("Keithley found at %1").arg(serial->portName())); } QElapsedTimer timer; timer.start(); while (!stop) { serial->write(":SENS:CHAN 1\r"); serial->write(":FETC?\r"); answer = ""; do { serial->waitForReadyRead(5); answer.append(serial->readAll()); } while (!answer.contains("\r")); answer = answer.replace("\r", ""); ui->vLabel->setText(answer); ui->msLabel->setText(QString::number(timer.restart())); qApp->processEvents(); } serial->close(); ui->vLabel->setText(""); ui->msLabel->setText(""); if(found) ui->statusBar->showMessage("Query closed", 5000); ui->startButton->setEnabled(true); ui->stoppButton->setEnabled(false); stop = false; } void COMPortChecker::on_stoppButton_clicked() { stop = true; }
-
@hkottmann
Your code relies onon_startButton_clicked()
running to end so that it executes the finalstop = false;
there. Does it even do so? It has ado { serial->waitForReadyRead(5); answer.append(serial->readAll()); } while (!answer.contains("\r"));
loop in it, what is going to make it exit that loop?
In a word, I would radically alter your
on_startButton_clicked()
code. It should be much smaller, no loops, nowaitForReadyRead()
s, nowhile (!stop)
or thatdo ... while ()
loop, no UI updating. And noprocessEvents()
. It is a slot on pushing a button. It should do no more than start the reading/writing process, maybe start some timer, and exit the slot. Anything further should be done in slots attached to signals for data arriving (readyRead()
) orQTimer::timeout
s. -
I haven't compiled and tested the application, but adding to what @JonB wrote:
There is a lot of heavy lifting with blocking code. Even though events are processed in blocking loops, UI reactions may be hampered. Recommend to sit back, check what needs to be done, which signals flag intermediate results.