How can I override QGuiApplication::notify()
According to my research this appears to be the way to actually catch exceptions that arise in a QT/C++ application - described in several places, e.g. https://stackoverflow.com/a/14804711/1495940
However, any description of it or examples I can find speak only of overriding QApplication::notify() or QCoreApplication::notify(), not QGuiApplication::nofity(). (Even though QGuiApplication inherits from QCoreApplication.
When I try to do it, my application crashes when I initialise QQuickView.
Here is what I have:
DebugApplication.h
#ifndef DEBUGAPPLICATION_H
#define DEBUGAPPLICATION_H
#include <QGuiApplication>
#include <typeinfo>
class DebugApplication : public QGuiApplication
{
public:
DebugApplication(int argc, char* argv[]);
bool notify(QObject* receiver, QEvent* e) override;
};
#endif // DEBUGAPPLICATION_H
DebugApplication.cpp:
#include "debugapplication.h"
DebugApplication::DebugApplication(int argc, char** argv) : QGuiApplication(argc, argv) {
}
bool DebugApplication::notify(QObject * receiver, QEvent* e)
{
try {
return QGuiApplication::notify(receiver, e);
}
catch (std::exception & ex) {
qFatal("Error %s sending event %s to object %s (%s)", ex.what(),
typeid(*e).name(),
qPrintable(receiver->objectName()),
typeid(*receiver).name()
);
}
catch (...) {
qFatal("Error <unknown> sending event %s to object %s (%s)",
typeid(*e).name(), qPrintable(receiver->objectName()),
typeid(*receiver).name());
}
return false;
}
And in main.cpp
qInstallMessageHandler( myMessageOutput ); // method to write messages to file.
DebugApplication app( argc, argv );
// Some initialisation code...
QQuickView view; // Crashes on this line.
view.rootContext()->setContextProperty( QStringLiteral( "contentHandler" ), (QObject *)(new contentLoader( view.rootContext(), &app )) );
view.setSource( QUrl( QStringLiteral( "qrc:/contentloader.qml" ) ) );
view.show();
// more stuff
return app.exec();
if I make app a QGuiApplication it works fine. But if I make it a DebugApplication it crashes at the QQuickView *view; line without explanation.
I will be just as happy with any other method of logging exceptions in the application! Basically my application crashes intermittently with no pattern or in no particular place and I'd like a way of logging info about why and where it crashed, but a regular try... catch just doesn't work, it just dies without falling into the catch.
It will display (but not log) an error message if I run in debug mode, but in release mode it just says "Process killed by signal" in QTCreator's Application output. Since I have never been able to recreate this crash myself, having an error in Debug mode doesn't help.
I also have a call to qInstallMessageHandler() which allows me to output qDebug() messages to a file but that doesn't help in this case unless I can catch the crash when it happens to write the output.
Related
I'm running a QWebEngineView in Qt 5.12 on a linux machine with Wayland, where a bug in Qt (there are a few issues reported about this) causes the app to freeze rendering if the screen is disconnected and reconnected, or if it goes into sleep mode.
The app still runs fine, but no further rendering is done ant it's stuck at last frame. The only errors that are visible when running the app are:
[14:52:07.809] connector 87 disconnected
QOpenGLFunctions created with non-current context
QPaintDevice::metrics: Device has no metric information
QQuickWidget: Cannot render due to failing makeCurrent()
QQuickWidget: Cannot render due to failing makeCurrent()
This is the place in Qt that throws this warning:
// qtdeclarative/src/quickwidgets/qquickwidget.cpp
void QQuickWidgetPrivate::render(bool needsSync)
{
if (!useSoftwareRenderer) {
#if QT_CONFIG(opengl)
// createFramebufferObject() bails out when the size is empty. In this case
// we cannot render either.
if (!fbo)
return;
Q_ASSERT(context);
if (!context->makeCurrent(offscreenSurface)) {
qWarning("QQuickWidget: Cannot render due to failing makeCurrent()");
return;
}
I unfortunately cannot alter the Qt version on this device, so I'd like to know if there is any reliable way of detecting this situation from inside the app to do some proper crash/restart?
I couldn't find any way to capture qWarning messages from inside of Qt or anything like that, and the code that causes this doesn't look as it could emit any interesting signal.
As last resort, I could parse the output of my app and look for "Cannot render due to failing makeCurrent()" but I really don't like that I cannot detect it from my own app that rendering has frozen.
I could add QT_FATAL_WARNINGS environment variable, which causes the app to automatically crash upon any warning, but I'm kinda scared that there may be some non-fatal warnings that would cause unintentional crashes.
You can use qInstallMessageHandler to override the default logging mechanism. And in there, you can check the qWarning messages for the specific string you're looking for.
void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
QByteArray localMsg = msg.toLocal8Bit();
const char *file = context.file ? context.file : "";
const char *function = context.function ? context.function : "";
switch (type) {
case QtWarningMsg:
fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
if (localMsg == theBadRenderError) {
// Do something here
}
break;
...
}
}
int main(int argc, char **argv)
{
qInstallMessageHandler(myMessageOutput);
QApplication app(argc, argv);
...
return app.exec();
}
I have a simple Qt/C++ program which gathers a webcam image out of one of my LAN devices using a cv::VideoCapture object.
The application is being built using Qt Quick and has an Image QML Item which is being served with a picture every 500 milliseconds via a custom QQuickImageProvider implementation:
WebcamImageProvider::WebcamImageProvider()
: QQuickImageProvider(ImageType::Image)
{
connect(&_timer, &QTimer::timeout, this, &WebcamImageProvider::updateImage);
// refresh our picture every .5 seconds
_timer.start(500);
}
// overridden function from base class
QImage WebcamImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
Q_UNUSED(id)
Q_UNUSED(size)
Q_UNUSED(requestedSize)
return _img;
}
My main function looks like this:
// Qt Quick
#include <QQuickItem>
#include <QQuickWindow>
// QML + GUI
#include <QQmlContext>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
// webcam stuff
#include "webcamimageprovider.h"
/*
* Here we go
*/
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
// we're adding our self-made image provider instance here
WebcamImageProvider wip;
engine.addImageProvider("webcam", &wip);
engine.rootContext()->setContextProperty("webcamImageProvider", &wip);
engine.load(url);
return app.exec();
The code of my main.qml file should not be important here.
Inside my image provider class, I have provided the updateImage slot as follows:
void WebcamImageProvider::updateImage()
{
/*
* Webcam image grapping via https://raspberrypi.stackexchange.com/a/85510/132942
*/
if (!_cap.open(_videoUrl)) {
qDebug() << "Error opening stream.";
}
if (!_cap.read(_mat)) {
qDebug() << "No video capture or webcam error.";
}
// via https://stackoverflow.com/a/12312326/4217759
_img = QImage(
static_cast<uchar*>(_mat.data),
_mat.cols,
_mat.rows,
static_cast<int>(_mat.step),
QImage::Format_BGR888
);
emit imageChanged(_img);
}
My problem here is that when my device is not reachable via network, the application will freeze completely as it gets stuck in the _cap.open() function. Therefore, I am trying to outsource this function to a future so the image will be loaded asynchronously. Since I have literally zero idea of threading and futures, I randomly gave it a shot with using QFutures:
void WebcamImageProvider::updateImage()
{
/*
* Webcam image grapping via https://raspberrypi.stackexchange.com/a/85510/132942
*/
QFuture<void> future = QtConcurrent::run([&](){ _cap.open(_videoUrl); });
if (!future.isRunning()) {
qDebug() << "Error opening stream.";
}
if (future.isFinished()) {
if (!_cap.read(_mat)) {
qDebug() << "No video capture or webcam error.";
}
// via https://stackoverflow.com/a/12312326/4217759
_img = QImage(
static_cast<uchar*>(_mat.data),
_mat.cols,
_mat.rows,
static_cast<int>(_mat.step),
QImage::Format_BGR888
);
emit imageChanged(_img);
}
}
However, I won't get any image output from that.
Can someone help me on how to structure the code correctly so I can load my image asynchronously here?
Thanks a lot in advance.
1- you should know something , create setter for _img and emit changed in that ! because it is possible one day you need set Image in another functions and if you don't do this , you should duplicate emit fooChanged().
2- when you check these and is's not responsible for working in your method , you should throw an exception then handle them and if you wanted use qDebug().
3- my suggest is that (if I understood completely your job) create an thread that working in loop and always getting new images then create a queue worker and create another thread (worker) for process your scenario(here updateImage method).
Is there any relatively "standard" design to auto restart a Qt application program, when it crashes abnormally?
Specific to Windows, do I have to use any windows service?
Or if I have to write another program separately, then how to do that?
Here's how you might do it using a single application that can act either as a monitor or as business logic. It's akin to Jon Harper's answer, except in code, not prose :)
Of Note
The monitor should not instantiate a QApplication nor QGuiApplication: it has no UI. Otherwise, redundant running process indicators will appear on some platforms (i.e. OS X, Win 10).
The monitor/business logic selection is achieved via setting an environment variable in the called process.
Passing the monitor/business logic selection via command line arguments is problematic, as the command line switch would need to be filtered out -- doing that portably without running into corner cases is tricky.
The monitor process forwards the console I/O of the business logic process, as well as the return code.
// https://github.com/KubaO/stackoverflown/tree/master/questions/appmonitor-37524491
#include <QtWidgets>
#include <cstdlib>
#if defined(Q_OS_WIN32)
#include <windows.h>
#else
static void DebugBreak() { abort(); }
#endif
static int businessLogicMain(int &argc, char **argv) {
QApplication app{argc, argv};
qDebug() << __FUNCTION__ << app.arguments();
QWidget w;
QHBoxLayout layout{&w};
QPushButton crash{"Crash"}; // purposefully crash for testing
QPushButton quit{"Quit"}; // graceful exit, which doesn't need restart
layout.addWidget(&crash);
layout.addWidget(&quit);
w.show();
QObject::connect(&crash, &QPushButton::clicked, DebugBreak);
QObject::connect(&quit, &QPushButton::clicked, &QCoreApplication::quit);
return app.exec();
}
static char const kRunLogic[] = "run__business__logic";
static char const kRunLogicValue[] = "run__business__logic";
#if defined(Q_OS_WIN32)
static QString getWindowsCommandLineArguments() {
const wchar_t *args = GetCommandLine();
bool oddBackslash = false, quoted = false, whitespace = false;
// skip the executable name according to Windows command line parsing rules
while (auto c = *args) {
if (c == L'\\')
oddBackslash ^= 1;
else if (c == L'"')
quoted ^= !oddBackslash;
else if (c == L' ' || c == L'\t')
whitespace = !quoted;
else if (whitespace)
break;
else
oddBackslash = false;
args++;
}
return QString::fromRawData(reinterpret_cast<const QChar*>(args), lstrlen(args));
}
#endif
static int monitorMain(int &argc, char **argv) {
#if !defined(Q_OS_WIN32)
QStringList args;
args.reserve(argc-1);
for (int i = 1; i < argc; ++i)
args << QString::fromLocal8Bit(argv[i]);
#endif
QCoreApplication app{argc, argv};
QProcess proc;
auto onFinished = [&](int retcode, QProcess::ExitStatus status) {
qDebug() << status;
if (status == QProcess::CrashExit)
proc.start(); // restart the app if the app crashed
else
app.exit(retcode); // no restart required
};
QObject::connect(&proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), onFinished);
auto env = QProcessEnvironment::systemEnvironment();
env.insert(kRunLogic, kRunLogicValue);
proc.setProgram(app.applicationFilePath()); // logic and monitor are the same executable
#if defined(Q_OS_WIN32)
SetErrorMode(SEM_NOGPFAULTERRORBOX); // disable Windows error reporting
proc.setNativeArguments(getWindowsCommandLineArguments()); // pass command line arguments natively
env.insert("QT_LOGGING_TO_CONSOLE", "1"); // ensure that the debug output gets passed along
#else
proc.setArguments(args);
#endif
proc.setProcessEnvironment(env);
proc.setProcessChannelMode(QProcess::ForwardedChannels);
proc.start();
return app.exec();
}
int main(int argc, char **argv) {
if (qgetenv(kRunLogic) != kRunLogicValue)
return monitorMain(argc, argv);
else
return qunsetenv(kRunLogic), businessLogicMain(argc, argv);
}
If an application crashes, it's done.
Your monitor idea is a good one, and can be achieved using QProcess. Use the "monitor" to bootstrap your actual application. To do this, implement a monitoring object with a QProcess member. In pseudocode:
class MonitorObject : public QObject
{
...
public Q_SLOTS:
void onStarted();
void onFinished(int, QProcess::ExitStatus);
...
private:
QProcess m_process;
}
Then in main:
Create a QCoreApplication and a monitoring object on the stack.
Send a queued signal to your monitor object so it knows when the main event loop starts. You can achieve this using QMetaObject::invoke with a Qt::QueuedConnection:
int main(...)
{
QCoreApplication app;
MonitorObject monitor;
... // other initialization code here
QMetaObject::invoke(&monitor, "onStarted", Qt::QueuedConnection);
return app.exec();
}
And in your MonitorObject:
Connect QProcess's finished signal to onFinished.
When MonitorObject::onStarted is called, start the process.
When the QProcess::finished signal fires, either restart the offending program or exit, depending on the exitCode argument in the emitted signal.
I don't know of any standard Qt method for restarting apps when they crash. But there is a nice class available which makes writing a supervisor/monitor class very easy. It is called QProcess.
You can start the process like this:
monitorClass::startProcess(QString commandLine) // e.g. "c:\mytestapp.exe param1 param2"
{
mp_Process = new QProcess(this);
mp_Process->start(commandLine);
mp_Process->waitForStarted();
// Start a timer
mp_Timer->start(1000);
}
Then when the timer expires (every second - or whatever)
void monitorClass::TimerExpired(void)
{
switch (mp_Process->state())
{
default:
case QProcess::NotRunning:
{
qDebug("Process has stopped un-expectedly\n");
// Tell the supervisor that the process has terminated
// restart the process
startProcess("c:\mytestapp.exe param1 param2"); // just an example
break;
}
case QProcess::Starting:
case QProcess::Running:
{
qDebug("Process is running ok\n");
break;
}
}
}
Note
This is really pseudo code, its not a compilable example - it is just to show you roughly the how easy it is to do this with QProcess...
I want to test a assertion with gtest.
The method looks like this:
void aMethod()
{
Q_ASSERT( 1 == geode.getNumDrawables());
DoSomeOtherStuff
}
And the test looks like this:
TEST_F(aTestClassDeathTest, aTestName)
{
::testing::FLAGS_gtest_death test_style = "threadsafe";
ASSERT_DEATH({ aMethod;}, "1 == geode.getNumDrawables");
}
This test works fine with gcc under linux.
But there is a problem with this test in visual studio 2010 under windows.
When I run the tests, an error window open. It shows an assertion failure. When I close the window, all tests finish. There are no test failures.
I think gtests starts a new process that cause the assertion failure and evaluates the process output. This works and the test pass.
But when visual studio notice that the new process fails, it creates the error windows.
How can I suppress the error window?
The popup window is due to Qt, not gtest. The Q_ASSERT macro is invoking the CRT debug window.
You can either replace your Q_ASSERT with assert or you can use a QtMsgHandler to suppress the popup.
As pointed out by other user (Fraser) in this thread, Googletest does not catch assertions thrown from Qt for ex. Q_ASSERT or Q_ASSERT_X. That is, user needs to take action on UI dialog shown from the Qt application.
QtMsgHandler comes to rescue. Here is the way you can go around this issue.
define a function as shown below:
void myMessageOutput(QtMsgType type, const char *msg)
{
switch (type)
{
case QtDebugMsg:
fprintf(stdout, "Debug: %s\n", msg); // you can also use stderr if you want
break;
case QtWarningMsg:
fprintf(stdout, "Warning: %s\n", msg);
break;
case QtCriticalMsg:
fprintf(stdout, "Critical: %s\n", msg);
break;
case QtFatalMsg:
fprintf(stdout, "Fatal: %s\n", msg);
abort();
}
}
In your Googletest application where you are expecting assertion call it in following manner:
// Redirect all messages generated from Qt to stdout
qInstallMsgHandler(myMessageOutput);
// Call death testcase
EXPECT_DEATH(call_causing_assertion(),"");
// Restore the default message handler
qInstallMsgHandler(0);
You can also make call in following manner to suppress all Qt assertion dialogs from test application:
int main(int argc, char **argv)
{
qInstallMsgHandler(myMessageOutput);
//QApplication app(argc, argv);
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
//...
//return app.exec();
}
i have simple application that start QDialog from its main like this :
int main(int argc, char *argv[])
{
Q_INIT_RESOURCE(resources);
QApplication app(argc, argv);
QCoreApplication::setApplicationName(APP_NAME);
QCoreApplication::setApplicationVersion(APP_VERISON);
QCoreApplication::setOrganizationDomain(APP_DOMAIN);
app.setStyle("WindowsXP");
QTextCodec::setCodecForTr(QTextCodec::codecForName("utf8"));
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
AuthenticationDialogContainer *pAuthenticationDialogContainer = new AuthenticationDialogContainer();
if(pAuthenticationDialogContainer->exec() != QDialog::Accepted ) {
return 0;
}
return app.exec();
}
when its pass the end of the application that is after app.exec() and the application doing what is suppose to do . when i open the windows xp task manager i see that the process is still in memory and i need manually kill it . how can i prevent it from happening ?
QDialog::exec is a blocking call: this code show and close the dialog before the QApplication start.
You can use QDialog::show and handle the return code in QDialog::accept method.