我可以使用QCommandLineParser确定GUI模式还是CLI模式?

时间:2018-10-04 14:39:10

标签: qt qt5

我使用的程序之一具有两种可以运行的模式:GUI(图形用户界面)模式或CLI(命令行界面)模式。我们通过命令行参数确定要使用的模式(即,如果传递了“ --cli”,它将使用CLI模式)。

实例化的QApplication的类型取决于所使用的模式:QApplication应该用于GUI模式,而QCoreApplication应该用于CLI模式,因为Qt的GUI部分不应该用于CLI模式(因为CLI模式不使用或不需要它们。

我可以通过类似于以下代码的方式做到这一点:

std::unique_ptr<QCoreApplication> app = 
    (cliMode) ? std::make_unique<QCoreApplication>(argc, argv)
              : std::make_unique<QApplication>(argc, argv);

// Do some other stuff...

return app->exec();

由于我已经在使用Qt,因此使用QCommandLineParser解析参数是有意义的。解析完参数后,我想对它们进行分析,以确定我们应该以GUI模式还是CLI模式运行。但是,这样做变得越来越困难。

我注意到的第一个问题是在Linux上的以下问题(在较旧版本的Qt5中没有发生,但在较新版本中确实发生了)

$ ./myQtApplication --help
QCoreApplication::arguments: Please instantiate the QApplication object first
Segmentation fault (core dumped)

好的:所以如果没有实例化QApplication对象,我将无法再运行--help命令。我通过手动解析参数以查看--help是否为参数来临时解决此问题。如果是这样,请继续实例化QCoreApplication,解析参数,然后退出。

但是随后我开始在Mac OS X上收到一个神秘的错误。当我直接在OS X上运行该可执行文件时,它可以毫无问题地运行。但是,如果我尝试双击.app文件或在终端机$ open myQtApplication.app中键入,则会收到此神秘错误:

LSOpenURLsWithRole() failed with error -10810 for the file ./myQtApplication.app

由于这是一个相当隐蔽的错误,我花了很长时间才弄清楚该错误是由实例化QApplication对象之前使用QCommandLineParser引起的。

要解决此问题,我现在正在执行以下操作:

  1. 手动解析main()函数开头的参数,以确定是否传递了--cli
  2. 根据#1的结果实例化QApplication对象。
  3. 运行QCommandLineParser来处理其余参数。

这不是一种很干净的方法,因为我现在有两个参数解析器:一个用于确定是否传递了--cli,其余的用于其他参数。

是否有更好的或“适当的”方法来做到这一点?

我猜主要问题是:我可以使用QCommandLineParser确定实例化QCoreApplication对象还是QApplication对象?

1 个答案:

答案 0 :(得分:4)

当然,您可以使用解析器-只要已经存在QCoreApplication。如果没有--cli选项,则将切换到QApplication。回想一下,您可以完全控制应用程序对象的生存期。

这在Windows和OS X的Qt 4.8和5.11下均有效:

// https://github.com/KubaO/stackoverflown/tree/master/questions/app-cli-gui-switch-52649458
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
#include <QtWidgets>
#endif

struct Options {
   bool cli;
};

static Options parseOptionsQt4() {
   Options opts = {};
   for (auto arg : QCoreApplication::arguments().mid(1)) {
      if (arg == "--cli")
         opts.cli = true;
      else
         qFatal("Unknown option %s", arg.toLocal8Bit().constData());
   }
   return opts;
}

static Options parseOptions() {
   if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) return parseOptionsQt4();
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
   Options opts = {};
   QCommandLineParser parser;
   QCommandLineOption cliOption("cli", "Start in command line mode.");
   parser.addOption(cliOption);
   parser.process(*qApp);
   opts.cli = parser.isSet(cliOption);
   return opts;
#endif
}

int main(int argc, char *argv[]) {
   QScopedPointer<QCoreApplication> app(new QCoreApplication(argc, argv));
   auto options = parseOptions();
   if (options.cli) {
      qDebug() << "cli";
   } else {
      qDebug() << "gui";
      app.reset();
      app.reset(new QApplication(argc, argv));
   }

   if (qobject_cast<QApplication *>(qApp))
      QMessageBox::information(nullptr, "Hello", "Hello, World!");
   QMetaObject::invokeMethod(qApp, "quit", Qt::QueuedConnection);
   return app->exec();
}