Skip to content

Commit bb6602b

Browse files
committed
Use QCommandLineParser in the qml runtime tool
Some advantages: - support double-hyphen options as well as the existing single-hyphen options - support translation of the usage text [ChangeLog][QtQml][qml] The QML Runtime tool now accepts command-line arguments in double-dash GNU style as well as the old single-dash style. Task-number: QTBUG-53557 Change-Id: I9929c63841272894640abe254efaea9773eee633 Reviewed-by: Ulf Hermann <[email protected]>
1 parent 679642f commit bb6602b

File tree

1 file changed

+122
-114
lines changed

1 file changed

+122
-114
lines changed

tools/qml/main.cpp

Lines changed: 122 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444

4545
#include <QQmlApplicationEngine>
4646
#include <QQmlComponent>
47+
#include <QCommandLineOption>
48+
#include <QCommandLineParser>
4749
#include <QDir>
4850
#include <QFile>
4951
#include <QFileInfo>
@@ -288,8 +290,6 @@ void quietMessageHandler(QtMsgType type, const QMessageLogContext &ctxt, const Q
288290
}
289291
}
290292

291-
292-
// ### Should command line arguments have translations? Qt creator doesn't, so maybe it's not worth it.
293293
enum QmlApplicationType {
294294
QmlApplicationTypeUnknown
295295
, QmlApplicationTypeCore
@@ -317,45 +317,6 @@ void printVersion()
317317
exit(0);
318318
}
319319

320-
void printUsage()
321-
{
322-
printf("Usage: qml [options] [files] [-- args]\n");
323-
printf("\n");
324-
printf("Any unknown argument before '--' will be treated as a QML file to be loaded.\n");
325-
printf("Any number of QML files can be loaded. They will share the same engine.\n");
326-
printf("'gui' application type is only available if the QtGui module is available.\n");
327-
printf("'widget' application type is only available if the QtWidgets module is available.\n");
328-
printf("\n");
329-
printf("General Options:\n");
330-
printf("\t-h, -help..................... Print this usage information and exit.\n");
331-
printf("\t-v, -version.................. Print the version information and exit.\n");
332-
#ifdef QT_GUI_LIB
333-
#ifndef QT_WIDGETS_LIB
334-
printf("\t-apptype [core|gui] .......... Select which application class to use. Default is gui.\n");
335-
#else
336-
printf("\t-apptype [core|gui|widget] ... Select which application class to use. Default is gui.\n");
337-
#endif // QT_WIDGETS_LIB
338-
#endif // QT_GUI_LIB
339-
printf("\t-quiet ....................... Suppress all output.\n");
340-
printf("\t-I [path] .................... Prepend the given path to the import paths.\n");
341-
printf("\t-f [file] .................... Load the given file as a QML file.\n");
342-
printf("\t-config [file] ............... Load the given file as the configuration file.\n");
343-
printf("\t-- ........................... Arguments after this one are ignored by the launcher, but may be used within the QML application.\n");
344-
printf("\tGL options:\n");
345-
printf("\t-desktop.......................Force use of desktop GL (AA_UseDesktopOpenGL)\n");
346-
printf("\t-gles..........................Force use of GLES (AA_UseOpenGLES)\n");
347-
printf("\t-software......................Force use of software rendering (AA_UseOpenGLES)\n");
348-
printf("\t-scaling.......................Enable High DPI scaling (AA_EnableHighDpiScaling)\n");
349-
printf("\t-no-scaling....................Disable High DPI scaling (AA_DisableHighDpiScaling)\n");
350-
printf("\tDebugging options:\n");
351-
printf("\t-verbose ..................... Print information about what qml is doing, like specific file urls being loaded.\n");
352-
printf("\t-translation [file] .......... Load the given file as the translations file.\n");
353-
printf("\t-dummy-data [directory] ...... Load QML files from the given directory as context properties.\n");
354-
printf("\t-slow-animations ............. Run all animations in slow motion.\n");
355-
printf("\t-fixed-animations ............ Run animations off animation tick rather than wall time.\n");
356-
exit(0);
357-
}
358-
359320
void noFilesGiven()
360321
{
361322
if (!quietMode)
@@ -368,7 +329,7 @@ void getAppFlags(int &argc, char **argv)
368329
{
369330
#ifdef QT_GUI_LIB
370331
for (int i=0; i<argc; i++) {
371-
if (!strcmp(argv[i], "-apptype")) { // Must be done before application, as it selects application
332+
if (!strcmp(argv[i], "--apptype") || !strcmp(argv[i], "-a") || !strcmp(argv[i], "-apptype")) {
372333
applicationType = QmlApplicationTypeUnknown;
373334
if (i+1 < argc) {
374335
if (!strcmp(argv[i+1], "core"))
@@ -380,15 +341,6 @@ void getAppFlags(int &argc, char **argv)
380341
applicationType = QmlApplicationTypeWidget;
381342
#endif // QT_WIDGETS_LIB
382343
}
383-
384-
if (applicationType == QmlApplicationTypeUnknown) {
385-
#ifndef QT_WIDGETS_LIB
386-
printf("-apptype must be followed by one of the following: core gui\n");
387-
#else
388-
printf("-apptype must be followed by one of the following: core gui widget\n");
389-
#endif // QT_WIDGETS_LIB
390-
printUsage();
391-
}
392344
for (int j=i; j<argc-2; j++)
393345
argv[j] = argv[j+2];
394346
argc -= 2;
@@ -442,9 +394,6 @@ int main(int argc, char *argv[])
442394
getAppFlags(argc, argv);
443395
QCoreApplication *app = nullptr;
444396
switch (applicationType) {
445-
case QmlApplicationTypeCore:
446-
app = new QCoreApplication(argc, argv);
447-
break;
448397
#ifdef QT_GUI_LIB
449398
case QmlApplicationTypeGui:
450399
app = new LoaderApplication(argc, argv);
@@ -456,8 +405,10 @@ int main(int argc, char *argv[])
456405
break;
457406
#endif // QT_WIDGETS_LIB
458407
#endif // QT_GUI_LIB
459-
default:
460-
Q_ASSERT_X(false, Q_FUNC_INFO, "impossible case");
408+
case QmlApplicationTypeCore:
409+
Q_FALLTHROUGH();
410+
default: // QmlApplicationTypeUnknown: not allowed, but we'll exit after checking apptypeOption below
411+
app = new QCoreApplication(argc, argv);
461412
break;
462413
}
463414

@@ -475,68 +426,125 @@ int main(int argc, char *argv[])
475426
QString dummyDir;
476427

477428
// Handle main arguments
478-
const QStringList argList = app->arguments();
479-
for (int i = 1; i < argList.count(); i++) {
480-
const QString &arg = argList[i];
481-
if (arg == QLatin1String("-quiet"))
482-
quietMode = true;
483-
else if (arg == QLatin1String("-v") || arg == QLatin1String("-version"))
484-
printVersion();
485-
else if (arg == QLatin1String("-h") || arg == QLatin1String("-help"))
486-
printUsage();
487-
else if (arg == QLatin1String("--"))
488-
break;
489-
else if (arg == QLatin1String("-verbose"))
490-
verboseMode = true;
429+
QCommandLineParser parser;
430+
parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
431+
parser.setOptionsAfterPositionalArgumentsMode(QCommandLineParser::ParseAsPositionalArguments);
432+
const QCommandLineOption helpOption = parser.addHelpOption();
433+
const QCommandLineOption versionOption = parser.addVersionOption();
434+
#ifdef QT_GUI_LIB
435+
QCommandLineOption apptypeOption(QStringList() << QStringLiteral("a") << QStringLiteral("apptype"),
436+
QCoreApplication::translate("main", "Select which application class to use. Default is gui."),
437+
#ifdef QT_WIDGETS_LIB
438+
QStringLiteral("core|gui|widget"));
439+
#else
440+
QStringLiteral("core|gui"));
441+
#endif // QT_WIDGETS_LIB
442+
parser.addOption(apptypeOption); // Just for the help text... we've already handled this argument above
443+
#endif // QT_GUI_LIB
444+
QCommandLineOption importOption(QStringLiteral("I"),
445+
QCoreApplication::translate("main", "Prepend the given path to the import paths."), QStringLiteral("path"));
446+
parser.addOption(importOption);
447+
QCommandLineOption qmlFileOption(QStringLiteral("f"),
448+
QCoreApplication::translate("main", "Load the given file as a QML file."), QStringLiteral("file"));
449+
parser.addOption(qmlFileOption);
450+
QCommandLineOption configOption(QStringList() << QStringLiteral("c") << QStringLiteral("config"),
451+
QCoreApplication::translate("main", "Load the given file as the configuration file."), QStringLiteral("file"));
452+
parser.addOption(configOption);
453+
QCommandLineOption translationOption(QStringLiteral("translation"),
454+
QCoreApplication::translate("main", "Load the given file as the translations file."), QStringLiteral("file"));
455+
parser.addOption(translationOption);
456+
QCommandLineOption dummyDataOption(QStringLiteral("dummy-data"),
457+
QCoreApplication::translate("main", "Load QML files from the given directory as context properties."), QStringLiteral("file"));
458+
parser.addOption(dummyDataOption);
459+
// OpenGL options
460+
QCommandLineOption glDesktopOption(QStringLiteral("desktop"),
461+
QCoreApplication::translate("main", "Force use of desktop OpenGL (AA_UseDesktopOpenGL)."));
462+
parser.addOption(glDesktopOption);
463+
QCommandLineOption glEsOption(QStringLiteral("gles"),
464+
QCoreApplication::translate("main", "Force use of GLES (AA_UseOpenGLES)."));
465+
parser.addOption(glEsOption);
466+
QCommandLineOption glSoftwareOption(QStringLiteral("software"),
467+
QCoreApplication::translate("main", "Force use of software rendering (AA_UseSoftwareOpenGL)."));
468+
parser.addOption(glSoftwareOption);
469+
QCommandLineOption scalingOption(QStringLiteral("scaling"),
470+
QCoreApplication::translate("main", "Enable High DPI scaling (AA_EnableHighDpiScaling)."));
471+
parser.addOption(scalingOption);
472+
QCommandLineOption noScalingOption(QStringLiteral("no-scaling"),
473+
QCoreApplication::translate("main", "Disable High DPI scaling (AA_DisableHighDpiScaling)."));
474+
parser.addOption(noScalingOption);
475+
// Debugging and verbosity options
476+
QCommandLineOption quietOption(QStringLiteral("quiet"),
477+
QCoreApplication::translate("main", "Suppress all output."));
478+
parser.addOption(quietOption);
479+
QCommandLineOption verboseOption(QStringLiteral("verbose"),
480+
QCoreApplication::translate("main", "Print information about what qml is doing, like specific file URLs being loaded."));
481+
parser.addOption(verboseOption);
482+
QCommandLineOption slowAnimationsOption(QStringLiteral("slow-animations"),
483+
QCoreApplication::translate("main", "Run all animations in slow motion."));
484+
parser.addOption(slowAnimationsOption);
485+
QCommandLineOption fixedAnimationsOption(QStringLiteral("fixed-animations"),
486+
QCoreApplication::translate("main", "Run animations off animation tick rather than wall time."));
487+
parser.addOption(fixedAnimationsOption);
488+
// Positional arguments
489+
parser.addPositionalArgument("files",
490+
QCoreApplication::translate("main", "Any number of QML files can be loaded. They will share the same engine."), "[files...]");
491+
parser.addPositionalArgument("args",
492+
QCoreApplication::translate("main", "Arguments after '--' are ignored, but passed through to the application.arguments variable in QML."), "[-- args...]");
493+
494+
if (!parser.parse(QCoreApplication::arguments())) {
495+
qWarning() << parser.errorText();
496+
exit(1);
497+
}
498+
if (parser.isSet(versionOption))
499+
parser.showVersion();
500+
if (parser.isSet(helpOption))
501+
parser.showHelp();
502+
if (applicationType == QmlApplicationTypeUnknown) {
503+
#ifdef QT_WIDGETS_LIB
504+
qWarning() << QCoreApplication::translate("main", "--apptype must be followed by one of the following: core gui widget\n");
505+
#else
506+
qWarning() << QCoreApplication::translate("main", "--apptype must be followed by one of the following: core gui\n");
507+
#endif // QT_WIDGETS_LIB
508+
parser.showHelp();
509+
}
510+
if (parser.isSet(verboseOption))
511+
verboseMode = true;
512+
if (parser.isSet(quietOption)) {
513+
quietMode = true;
514+
verboseMode = false;
515+
}
491516
#if QT_CONFIG(qml_animation)
492-
else if (arg == QLatin1String("-slow-animations"))
493-
QUnifiedTimer::instance()->setSlowModeEnabled(true);
494-
else if (arg == QLatin1String("-fixed-animations"))
495-
QUnifiedTimer::instance()->setConsistentTiming(true);
517+
if (parser.isSet(slowAnimationsOption))
518+
QUnifiedTimer::instance()->setSlowModeEnabled(true);
519+
if (parser.isSet(fixedAnimationsOption))
520+
QUnifiedTimer::instance()->setConsistentTiming(true);
496521
#endif
497-
else if (arg == QLatin1String("-I")) {
498-
if (i+1 == argList.count())
499-
continue; // Invalid usage, but just ignore it
500-
e.addImportPath(argList[i+1]);
501-
i++;
502-
} else if (arg == QLatin1String("-f")) {
503-
if (i+1 == argList.count())
504-
continue; // Invalid usage, but just ignore it
505-
files << argList[i+1];
506-
i++;
507-
} else if (arg == QLatin1String("-config")){
508-
if (i+1 == argList.count())
509-
continue; // Invalid usage, but just ignore it
510-
confFile = argList[i+1];
511-
i++;
512-
} else if (arg == QLatin1String("-translation")){
513-
if (i+1 == argList.count())
514-
continue; // Invalid usage, but just ignore it
515-
translationFile = argList[i+1];
516-
i++;
517-
} else if (arg == QLatin1String("-dummy-data")){
518-
if (i+1 == argList.count())
519-
continue; // Invalid usage, but just ignore it
520-
dummyDir = argList[i+1];
521-
i++;
522-
} else if (arg == QLatin1String("-gles")) {
523-
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
524-
} else if (arg == QLatin1String("-software")) {
525-
QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
526-
} else if (arg == QLatin1String("-desktop")) {
527-
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
528-
} else if (arg == QLatin1String("-scaling")) {
529-
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
530-
} else if (arg == QLatin1String("-no-scaling")) {
531-
QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
532-
} else {
533-
files << arg;
534-
}
522+
if (parser.isSet(glEsOption))
523+
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
524+
if (parser.isSet(glSoftwareOption))
525+
QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
526+
if (parser.isSet(glDesktopOption))
527+
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
528+
if (parser.isSet(scalingOption))
529+
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
530+
if (parser.isSet(noScalingOption))
531+
QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
532+
for (const QString &importPath : parser.values(importOption))
533+
e.addImportPath(importPath);
534+
files << parser.values(qmlFileOption);
535+
if (parser.isSet(configOption))
536+
confFile = parser.value(configOption);
537+
if (parser.isSet(translationOption))
538+
translationFile = parser.value(translationOption);
539+
if (parser.isSet(dummyDataOption))
540+
dummyDir = parser.value(dummyDataOption);
541+
for (QString posArg : parser.positionalArguments()) {
542+
if (posArg == QLatin1String("--"))
543+
break;
544+
else
545+
files << posArg;
535546
}
536547

537-
if (quietMode && verboseMode)
538-
verboseMode = false;
539-
540548
#if QT_CONFIG(translation)
541549
// Need to be installed before QQmlApplicationEngine's automatic translation loading
542550
// (qt_ translations are loaded there)

0 commit comments

Comments
 (0)