Added dialogs when starting/stopping sensors/detection. Fixed nullptr bug on MainWindow::handleEvents

This commit is contained in:
matlabbe
2026-07-03 19:24:45 -07:00
parent d868cffc76
commit 5d74d9e153
5 changed files with 121 additions and 10 deletions
+4
View File
@@ -52,6 +52,10 @@ int main(int argc, char* argv[])
ULogger::setLevel(ULogger::kWarning); ULogger::setLevel(ULogger::kWarning);
#ifdef WIN32 #ifdef WIN32
// STA (single-threaded apartment) is required for the native file dialogs / File Explorer
// (see commit d75cc04, "Fixed File Explorer hanging (Qt 5.12)"). Do NOT switch to MTA
// (CoInitializeEx COINIT_MULTITHREADED): it deadlocks the
// native file dialogs.
CoInitialize(nullptr); CoInitialize(nullptr);
#endif #endif
+34 -1
View File
@@ -78,6 +78,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QtCore/QFileInfo> #include <QtCore/QFileInfo>
#include <QMessageBox> #include <QMessageBox>
#include <QFileDialog> #include <QFileDialog>
#include <QProgressDialog>
#include <QGraphicsEllipseItem> #include <QGraphicsEllipseItem>
#include <QDockWidget> #include <QDockWidget>
#include <QtCore/QBuffer> #include <QtCore/QBuffer>
@@ -958,7 +959,7 @@ bool MainWindow::handleEvent(UEvent* anEvent)
else else
{ {
Q_EMIT cameraInfoReceived(sensorEvent->info()); Q_EMIT cameraInfoReceived(sensorEvent->info());
if (_odomThread == 0 && (_sensorCapture->odomProvided()) && _preferencesDialog->isRGBDMode()) if (_odomThread == 0 && _sensorCapture && _sensorCapture->odomProvided() && _preferencesDialog->isRGBDMode())
{ {
OdometryInfo odomInfo; OdometryInfo odomInfo;
odomInfo.reg.covariance = sensorEvent->info().odomCovariance; odomInfo.reg.covariance = sensorEvent->info().odomCovariance;
@@ -5932,6 +5933,18 @@ void MainWindow::startDetection()
Camera * camera = 0; Camera * camera = 0;
Lidar * lidar = 0; Lidar * lidar = 0;
// Creating the sensors below opens the devices (createLidar/createCamera/createOdomSensor ->
// init(), a few seconds for ZED/RealSense) on the GUI thread; show a busy dialog (min==max==0
// => indeterminate) so the window isn't just frozen. Hidden once all sensors are created below.
QProgressDialog progress(tr("Starting detection..."), QString(), 0, 0, this);
progress.setWindowModality(Qt::ApplicationModal);
progress.setCancelButton(0);
progress.setMinimumDuration(0);
progress.setValue(0);
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
if(_preferencesDialog->getLidarSourceDriver() != PreferencesDialog::kSrcUndef) if(_preferencesDialog->getLidarSourceDriver() != PreferencesDialog::kSrcUndef)
{ {
lidar = _preferencesDialog->createLidar(); lidar = _preferencesDialog->createLidar();
@@ -5989,6 +6002,8 @@ void MainWindow::startDetection()
} }
} }
progress.hide(); // all sensors created/opened
_sensorCapture = new SensorCaptureThread(lidar, camera, odomSensor, extrinsics, poseTimeOffset, scaleFactor, waitTime, parameters); _sensorCapture = new SensorCaptureThread(lidar, camera, odomSensor, extrinsics, poseTimeOffset, scaleFactor, waitTime, parameters);
_sensorCapture->setOdomAsGroundTruth(_preferencesDialog->isOdomSensorAsGt()); _sensorCapture->setOdomAsGroundTruth(_preferencesDialog->isOdomSensorAsGt());
@@ -6229,6 +6244,24 @@ void MainWindow::stopDetection()
} }
ULOGGER_DEBUG(""); ULOGGER_DEBUG("");
// Closing the camera runs on the GUI thread in "delete _sensorCapture" below (via
// ~SensorCaptureThread -> ~Camera::close()) and can block for a while - e.g. the first 2-3
// RealSense closes per launch stall ~20s in the Motion Module stop() (librealsense warm-up).
// Show a busy dialog (min==max==0 => indeterminate) so the window isn't just frozen. It is
// declared here so it stays visible across the joins/deletes and closes on scope exit.
QProgressDialog progress(tr("Stopping detection..."), QString(), 0, 0, this);
if(_sensorCapture)
{
progress.setWindowModality(Qt::ApplicationModal);
progress.setCancelButton(0);
progress.setMinimumDuration(0);
progress.setValue(0);
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
}
// kill the processes // kill the processes
if(_imuThread) if(_imuThread)
{ {
+77 -7
View File
@@ -49,6 +49,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QtGui/QStandardItemModel> #include <QtGui/QStandardItemModel>
#include <QMainWindow> #include <QMainWindow>
#include <QProgressDialog> #include <QProgressDialog>
#include <QApplication>
#include <functional>
#include <QScrollBar> #include <QScrollBar>
#include <QStatusBar> #include <QStatusBar>
#include <QFormLayout> #include <QFormLayout>
@@ -6837,11 +6839,11 @@ double PreferencesDialog::getSourceScanForceGroundNormalsUp() const
Camera * PreferencesDialog::createCamera(bool useRawImages, bool useColor) Camera * PreferencesDialog::createCamera(bool useRawImages, bool useColor)
{ {
return createCamera( return createCamera(
this->getSourceDriver(), this->getSourceDriver(),
_ui->lineEdit_sourceDevice->text(), _ui->lineEdit_sourceDevice->text(),
_ui->lineEdit_calibrationFile->text(), _ui->lineEdit_calibrationFile->text(),
useRawImages, useRawImages,
useColor, useColor,
false, false,
false); false);
} }
@@ -7745,7 +7747,20 @@ void PreferencesDialog::setSLAMMode(bool enabled)
void PreferencesDialog::testOdometry() void PreferencesDialog::testOdometry()
{ {
// One progress dialog, reused for both the (slow) camera init and the (slow) close.
// Declared here so it outlives cameraThread below and stays visible while cameraThread's
// destructor closes the device at function scope end.
QProgressDialog progress(tr("Starting camera..."), QString(), 0, 0, this);
progress.setWindowModality(Qt::ApplicationModal);
progress.setCancelButton(0);
progress.setMinimumDuration(0);
progress.setValue(0);
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
Camera * camera = this->createCamera(); Camera * camera = this->createCamera();
progress.hide();
if(!camera) if(!camera)
{ {
return; return;
@@ -7861,6 +7876,7 @@ void PreferencesDialog::testOdometry()
} }
odomViewer->exec(); odomViewer->exec();
UDEBUG("Dialog closed, stopping sensor...");
// Tear down the pipes first so no more events are routed to the threads/viewer being // Tear down the pipes first so no more events are routed to the threads/viewer being
// destroyed, then stop the threads, then delete the viewer. This avoids delivering // destroyed, then stop the threads, then delete the viewer. This avoids delivering
@@ -7877,6 +7893,14 @@ void PreferencesDialog::testOdometry()
{ {
imuThread->join(true); imuThread->join(true);
} }
// Reuse the same dialog for the close. The device close() runs in cameraThread's destructor
// at function scope end (not in join()), so 'progress' stays visible across it. On Windows
// the first 2-3 RealSense closes per launch stall ~20s in the Motion Module stop().
progress.setLabelText(tr("Closing camera..."));
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
cameraThread.join(true); cameraThread.join(true);
odomThread.join(true); odomThread.join(true);
@@ -7900,7 +7924,21 @@ void PreferencesDialog::testCamera()
window->resize(1280, 480+QPushButton().minimumHeight()); window->resize(1280, 480+QPushButton().minimumHeight());
window->registerToEventsManager(); window->registerToEventsManager();
// One progress dialog, reused for both the (slow) camera init and the (slow) close.
// min==max==0 => indeterminate/busy bar. Declared in this outer scope so it outlives
// cameraThread below and stays visible while cameraThread's destructor closes the device.
QProgressDialog progress(tr("Starting camera..."), QString(), 0, 0, this);
progress.setWindowModality(Qt::ApplicationModal);
progress.setCancelButton(0);
progress.setMinimumDuration(0);
progress.setValue(0);
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
// createCamera() init()s the device on the GUI thread (required by ZED) and takes a few seconds.
Camera * camera = this->createCamera(); Camera * camera = this->createCamera();
progress.hide();
if(camera) if(camera)
{ {
SensorCaptureThread cameraThread(camera, this->getAllParameters()); SensorCaptureThread cameraThread(camera, this->getAllParameters());
@@ -7945,8 +7983,18 @@ void PreferencesDialog::testCamera()
cameraThread.start(); cameraThread.start();
window->exec(); window->exec();
UDEBUG("Dialog closed, stopping sensor...");
UEventsManager::removePipe(&cameraThread, window, "SensorEvent"); UEventsManager::removePipe(&cameraThread, window, "SensorEvent");
cameraThread.join(true);
// Reuse the same dialog for the close. The device close() runs in cameraThread's
// destructor at scope end (not in join()), so 'progress' - declared in the outer scope -
// stays visible across it. On Windows the first 2-3 RealSense closes per launch stall
// ~20s in the Motion Module stop() (librealsense warm-up); this keeps the user informed.
progress.setLabelText(tr("Closing camera..."));
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
cameraThread.join(true); // cameraThread's destructor (scope end) closes the device
// deleteLater() (not delete): defer destruction to the event loop so Qt finishes // deleteLater() (not delete): defer destruction to the event loop so Qt finishes
// tearing down the OpenGL widget's context and window-proc subclass and drains // tearing down the OpenGL widget's context and window-proc subclass and drains
// pending activation messages first. // pending activation messages first.
@@ -8428,7 +8476,20 @@ void PreferencesDialog::testLidar()
window->registerToEventsManager(); window->registerToEventsManager();
window->setDecimation(1); window->setDecimation(1);
// One progress dialog, reused for both the (slow) sensor init and the (slow) close.
// Declared in this outer scope so it outlives lidarThread below and stays visible while
// lidarThread's destructor closes the device.
QProgressDialog progress(tr("Starting sensor..."), QString(), 0, 0, this);
progress.setWindowModality(Qt::ApplicationModal);
progress.setCancelButton(0);
progress.setMinimumDuration(0);
progress.setValue(0);
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
Lidar * lidar = this->createLidar(); Lidar * lidar = this->createLidar();
progress.hide();
if(lidar) if(lidar)
{ {
SensorCaptureThread lidarThread(lidar, this->getAllParameters()); SensorCaptureThread lidarThread(lidar, this->getAllParameters());
@@ -8447,8 +8508,17 @@ void PreferencesDialog::testLidar()
lidarThread.start(); lidarThread.start();
window->exec(); window->exec();
UDEBUG("Dialog closed, stopping sensor...");
UEventsManager::removePipe(&lidarThread, window, "SensorEvent"); UEventsManager::removePipe(&lidarThread, window, "SensorEvent");
lidarThread.join(true);
// Reuse the same dialog for the close. The device close() runs in lidarThread's
// destructor at scope end (not in join()), so 'progress' - declared in the outer
// scope - stays visible across it.
progress.setLabelText(tr("Closing sensor..."));
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
lidarThread.join(true); // lidarThread's destructor (scope end) closes the device
// deleteLater() (not delete): see testCamera() - avoids a dangling OpenGL platform // deleteLater() (not delete): see testCamera() - avoids a dangling OpenGL platform
// window that crashes in QWindowsWindow::alertWindow when Preferences later closes. // window that crashes in QWindowsWindow::alertWindow when Preferences later closes.
window->deleteLater(); window->deleteLater();
+3 -2
View File
@@ -97,7 +97,7 @@ void sighandler(int sig)
int main(int argc, char * argv[]) int main(int argc, char * argv[])
{ {
ULogger::setType(ULogger::kTypeConsole); ULogger::setType(ULogger::kTypeConsole);
ULogger::setLevel(ULogger::kInfo); ULogger::setLevel(ULogger::kDebug);
//ULogger::setPrintTime(false); //ULogger::setPrintTime(false);
//ULogger::setPrintWhere(false); //ULogger::setPrintWhere(false);
@@ -272,7 +272,7 @@ int main(int argc, char * argv[])
UERROR("Not built with ZED sdk support..."); UERROR("Not built with ZED sdk support...");
exit(-1); exit(-1);
} }
camera = new rtabmap::CameraStereoZed(deviceId.empty()?0:uStr2Int(deviceId), -1, 1, 100, false, rate); camera = new rtabmap::CameraStereoZed(deviceId.empty()?0:uStr2Int(deviceId), -1, 1, 0, 100, false, rate);
} }
else if (driver == 9) else if (driver == 9)
{ {
@@ -523,5 +523,6 @@ int main(int argc, char * argv[])
cameraViewer.exec(); cameraViewer.exec();
cameraThread.join(true); cameraThread.join(true);
} }
printf("Exiting cleanly.\n");
return 0; return 0;
} }
+3
View File
@@ -404,13 +404,16 @@ int main (int argc, char * argv[])
{ {
printf("The camera is not calibrated! You should calibrate the camera first.\n"); printf("The camera is not calibrated! You should calibrate the camera first.\n");
delete camera; delete camera;
return 1;
} }
} }
else else
{ {
printf("Failed to initialize the camera! Please select another driver (see \"--help\").\n"); printf("Failed to initialize the camera! Please select another driver (see \"--help\").\n");
delete camera; delete camera;
return 1;
} }
printf("Exiting cleanly.\n");
return 0; return 0;
} }