Qt - capturing internal qWarning or "failing makeCurrent()" error - c++

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();
}

Related

Catch exceptions in QT/C++ (QGuiApplication) (override notify() method?)

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.

Why is QMediaPlayer::duration() giving me a value of -1?

Here is my code:
QString BoatProgramming::setDuration(QString path)
{
if (path.isNull()) { return ""; }
QMediaPlayer mp;
mp.setMedia(QUrl::fromLocalFile("/home/akiva/deleteme.ogg"));
qDebug() << mp.duration(); // Outputting a value of -1
m_Duration = QString::number(mp.duration());
emit durationChanged();
return m_Duration;
}
There is obviously an error somewhere, but beyond checking the filename, I am lamentably amiss as to what the problem is. Could it be simply that .ogg is not supported? Am I calling the function before the object is fully loaded into memory? Or is it something else?
Thanks.
QMediaPlayer::setMedia() performs loading asynchronously, from the docs:
This function returns immediately after recording the specified source of the media. It does not wait for the media to finish loading and does not check for errors. Listen for the mediaStatusChanged() and error() signals to be notified when the media is loaded and when an error occurs during loading.
This means that querying QMediaPlayer for duration() after calling setMedia() immediately may not work, since QMediaPlayer might not have loaded the media yet.
In order to guarantee that loading has finished before calling duration(), you have to listen for mediaStatusChanged() signal, and get duration() only when mediaStatus() returns QMediaPlayer::LoadedMedia. Here is a minimal example:
#include <QtWidgets>
#include <QtMultimedia>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QLabel label; //a label to display duration
QString fileName = QFileDialog::getOpenFileName(nullptr, "Open Media File");
QMediaPlayer mp;
mp.setMedia(QUrl::fromLocalFile(fileName));
QObject::connect(&mp, &QMediaPlayer::mediaStatusChanged,
[&](QMediaPlayer::MediaStatus status){
if(status == QMediaPlayer::LoadedMedia) //when loading is finished
{
//show duration in a label
qint64 duration= mp.duration();
label.setText(QString("Duration: %1 ms.\n\nThat is: %2")
.arg(duration)
.arg(QDateTime::fromTime_t(duration/1000).toUTC()
.toString("hh:mm:ss")));
label.show();
}
});
return app.exec();
}

How to auto restart a Qt application when it crashes, within the same program?

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...

Qt-App on Windows Boot: Touchscreen generates mouse move to but no click

We have a Qt GUI application being started on boot through registry Run/RunOnce-Key.
When started on boot the Qt application gets position from touch screen but does not get the click event (emulated mouse click on button) itself.
When run manually the Qt application gets position and click from touch screen.
When using the mouse both version work fine. Other applications started on boot do accept the click through touch.
We do not have any TouchEvents implemented, the touchscreen is just interpreted as mouse events. I guess the application is just to quick and starts before the touchscreen driver being completly loaded and somehow bugs the "click" event then. But i don't know how to validate this or how to look for the problem at all.
We run different systems where this problem does not occur, its just this one windows pc we have the trouble with now. Changing the pc is an option for removing the problem, but i still want to find the source of the problem to make sure this won't happen again. Currently its one out of 100 systems having this problem.
I wrote a small additional application installing an eventFilter to qApp to see what events actually arrive. When run on boot i only receive mouse-move events, while started manually later i receive move press release events.
I wonder if someone else has encountered similar touch problems with Qt/"clickapplications" and touchscreens itself.
bool MouseFilter::eventFilter(QObject *o, QEvent *e)
{
if(e->type() == QEvent::MouseMove)
{
emit signalMouseMove();
return true;
}else if(e->type() == QEvent::MouseButtonRelease) {
emit signalMouseRelease();
return true;
}else if(e->type() == QEvent::MouseButtonPress) {
emit signalMousePress();
return true;
}else if(e->type() == QEvent::MouseButtonDblClick) {
emit signalMouseDoubleClick();
return true;
}
return QObject::eventFilter(o,e);
}
MouseFilter *mf = new MouseFilter();
qApp->installEventFilter(mf);
Problem "solved" by (workaround) adding a sleep timer before QApplication call. The problem seems to be connected to the super fast boot using SSD and autostart using registry Run-key while touchscreen drivers still seem to be loading.
Important: The sleep call has to be made before QApplication is created, otherwise you just keep having the problem.
int main(int argc, char *argv[])
{
QThread::sleep(15);
QApplication a(argc, argv);
if(!QDir::setCurrent(QApplication::applicationDirPath()))
QDir::setCurrent("C:\\ApplicationPath\\");
Translator *t = new Translator();
a.installEventFilter(t);
return a.exec();
}
Qt Support recommends the to check for GetSystemMetrics(QT_SM_DIGITIZER) using windows native functions. I extended this a bit to have a nice Splash Screen so user has a notification something is happening:
static inline bool hasTouchSupport(QSysInfo::WinVersion wv)
{
enum { QT_SM_DIGITIZER = 94, QT_NID_INTEGRATED_TOUCH = 0x1,
QT_NID_EXTERNAL_TOUCH = 0x02, QT_NID_MULTI_INPUT = 0x40 };
return wv < QSysInfo::WV_WINDOWS7 ? false :
(GetSystemMetrics(QT_SM_DIGITIZER) & (QT_NID_INTEGRATED_TOUCH | QT_NID_EXTERNAL_TOUCH | QT_NID_MULTI_INPUT)) != 0;
}
int main(int argc, char *argv[])
{
if(!hasTouchSupport(QSysInfo::windowsVersion())) {
QApplication *ta = new QApplication(argc,argv);
QPixmap logo("C:\\PathToLogo\\LogoTransparent.png");
QSplashScreen splash(logo);
splash.show();
clock_t start = clock();
while(30*CLOCKS_PER_SEC > clock()-start)
{
if(hasTouchSupport(QSysInfo::windowsVersion()))
break;
ta->processEvents();
QThread::msleep(20);
}
splash.close();
ta->quit();
ta->processEvents();
delete ta;
}
QApplication *a = new QApplication(argc, argv);
//Custom Code goes here
return a->exec();
}

Googletest - DeathTest does not catch assertion from Qt application such as Q_ASSERT etc.

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();
}