After releasing Nu, Pogodi! I learned the hard way that checking the QML runtime errors might be a good idea. For that particular application, simply checking the errors from QDeclarativeView after setting the main qml file was enough, because everything in qml file was statically declared. But what if you use QML Loader element, either explicitly or through some other qml element like PageStack from Qt Components, and something goes wrong?
Well, if you don’t improve the error handling code, your application will silently fail in some places, which probably won’t make the users happy. I didn’t wanted to repeat the Nu, Pogodi! screw up when releasing Word Judge, so I’ve created a better error handling solution. First part is an error handler class:
// ----------------------------------------// qmlerrorhandler.h// ----------------------------------------classQmlErrorHandler:publicQObject{Q_OBJECTpublic:explicitQmlErrorHandler(QDeclarativeView&view,QObject*parent=0);boolerrorOccured()const;privateslots:voidhandleQmlStatusChange(QDeclarativeView::Statusstatus);voidhandleQmlErrors(constQList<QDeclarativeError>&qmlErrors);private:QDeclarativeView&mView;boolmErrorOccured;};// ----------------------------------------// qmlerrorhandler.cpp// ----------------------------------------QmlErrorHandler::QmlErrorHandler(QDeclarativeView&view,QObject*parent):QObject(parent),mView(view),mErrorOccured(false){connect(&view,SIGNAL(statusChanged(QDeclarativeView::Status)),SLOT(handleQmlStatusChange(QDeclarativeView::Status)));connect(view.engine(),SIGNAL(warnings(QList<QDeclarativeError>)),SLOT(handleQmlErrors(QList<QDeclarativeError>)));}voidQmlErrorHandler::handleQmlStatusChange(QDeclarativeView::Statusstatus){if(status==QDeclarativeView::Error){handleQmlErrors(mView.errors());}}voidQmlErrorHandler::handleQmlErrors(constQList<QDeclarativeError>&qmlErrors){QStringListerrors;foreach(constQDeclarativeError&error,qmlErrors){// Special case for bug in QtComponents 1.1// https://bugreports.qt-project.org/browse/QTCOMPONENTS-1217if(error.url().toString().endsWith("PageStackWindow.qml")&&error.line()==70)continue;errors.append(error.toString());}if(errors.isEmpty())return;mErrorOccured=true;QMessageBoxmsgBox;msgBox.setText("Uh oh, something went terribly wrong!");msgBox.setInformativeText("We're sorry, but it seems there are some problems ""with running our application on your phone. Please ""send us the following information to help us resolve ""this issue:\n\n")+errors.join("\n"));msgBox.exec();qApp->exit(-1);}boolQmlErrorHandler::errorOccured()const{returnmErrorOccured;}
Basically we need to catch the runtime errors, which are emitted from QDeclarativeEngine in signal named for some unfathomable reason warnings. Checking the errorOccured() in main() is ugly, but the qApp->exit() doesn’t work until the event loop in main is started and that’s the first thing which came to my mind. Please leave a comment if you know a simpler solution.
Note the lines 46-49 in QmlErrorHandler: we’re catching all warnings and the qt components are not completely free of them. I had to add a special case to prevent triggering the handler on every orientation change. If you stumble upon some other errors that should be ignored, please let me know.