Use QCommandLineParser in example dnslookup.

Show how use QCommandLineParser with additional
parameter checking for custom options and positional
arguments.

Also explain how to display help in GUI applications.

Change-Id: I03513e09b7dd5b150259593da0af2ef2a281cab2
Reviewed-by: David Faure <david.faure@kdab.com>
This commit is contained in:
Friedemann Kleint 2014-03-11 09:35:35 +01:00 committed by The Qt Project
parent 00fe7bd975
commit 51e5378113
4 changed files with 210 additions and 56 deletions

View File

@ -45,14 +45,101 @@
#include <QHostAddress>
#include <QStringList>
#include <QTimer>
#include <QCommandLineParser>
#include <QCommandLineOption>
#include <stdio.h>
static void usage() {
printf("Qt DNS example - performs DNS lookups\n"
"Usage: dnslookup [-t <type>] [-s nameserver] name\n\n");
static int typeFromParameter(const QString &type)
{
if (type == "a")
return QDnsLookup::A;
if (type == "aaaa")
return QDnsLookup::AAAA;
if (type == "any")
return QDnsLookup::ANY;
if (type == "cname")
return QDnsLookup::CNAME;
if (type == "mx")
return QDnsLookup::MX;
if (type == "ns")
return QDnsLookup::NS;
if (type == "ptr")
return QDnsLookup::PTR;
if (type == "srv")
return QDnsLookup::SRV;
if (type == "txt")
return QDnsLookup::TXT;
return -1;
}
//! [0]
enum CommandLineParseResult
{
CommandLineOk,
CommandLineError,
CommandLineVersionRequested,
CommandLineHelpRequested
};
CommandLineParseResult parseCommandLine(QCommandLineParser &parser, DnsQuery *query, QString *errorMessage)
{
parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
const QCommandLineOption nameServerOption("n", "The name server to use.", "nameserver");
parser.addOption(nameServerOption);
const QCommandLineOption typeOption("t", "The lookup type.", "type");
parser.addOption(typeOption);
parser.addPositionalArgument("name", "The name to look up.");
const QCommandLineOption helpOption = parser.addHelpOption();
const QCommandLineOption versionOption = parser.addVersionOption();
if (!parser.parse(QCoreApplication::arguments())) {
*errorMessage = parser.errorText();
return CommandLineError;
}
if (parser.isSet(versionOption))
return CommandLineVersionRequested;
if (parser.isSet(helpOption))
return CommandLineHelpRequested;
if (parser.isSet(nameServerOption)) {
const QString nameserver = parser.value(nameServerOption);
query->nameServer = QHostAddress(nameserver);
if (query->nameServer.isNull() || query->nameServer.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol) {
*errorMessage = "Bad nameserver address: " + nameserver;
return CommandLineError;
}
}
if (parser.isSet(typeOption)) {
const QString typeParameter = parser.value(typeOption);
const int type = typeFromParameter(typeParameter.toLower());
if (type < 0) {
*errorMessage = "Bad record type: " + typeParameter;
return CommandLineError;
}
query->type = static_cast<QDnsLookup::Type>(type);
}
const QStringList positionalArguments = parser.positionalArguments();
if (positionalArguments.isEmpty()) {
*errorMessage = "Argument 'name' missing.";
return CommandLineError;
}
if (positionalArguments.size() > 1) {
*errorMessage = "Several 'name' arguments specified.";
return CommandLineError;
}
query->name = positionalArguments.first();
return CommandLineOk;
}
//! [0]
DnsManager::DnsManager()
{
dns = new QDnsLookup(this);
@ -61,55 +148,11 @@ DnsManager::DnsManager()
void DnsManager::execute()
{
QStringList args = QCoreApplication::instance()->arguments();
args.takeFirst();
// lookup type
dns->setType(QDnsLookup::A);
if (args.size() > 1 && args.first() == "-t") {
args.takeFirst();
const QString type = args.takeFirst().toLower();
if (type == "a")
dns->setType(QDnsLookup::A);
else if (type == "aaaa")
dns->setType(QDnsLookup::AAAA);
else if (type == "any")
dns->setType(QDnsLookup::ANY);
else if (type == "cname")
dns->setType(QDnsLookup::CNAME);
else if (type == "mx")
dns->setType(QDnsLookup::MX);
else if (type == "ns")
dns->setType(QDnsLookup::NS);
else if (type == "ptr")
dns->setType(QDnsLookup::PTR);
else if (type == "srv")
dns->setType(QDnsLookup::SRV);
else if (type == "txt")
dns->setType(QDnsLookup::TXT);
else {
printf("Bad record type: %s\n", qPrintable(type));
QCoreApplication::instance()->quit();
return;
}
}
if (args.size() > 1 && args.first() == "-s") {
args.takeFirst();
const QString ns = args.takeFirst();
QHostAddress nameserver(ns);
if (nameserver.isNull() || nameserver.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol) {
printf("Bad nameserver address: %s\n", qPrintable(ns));
QCoreApplication::instance()->quit();
return;
}
dns->setNameserver(nameserver);
}
if (args.isEmpty()) {
usage();
QCoreApplication::instance()->quit();
return;
}
dns->setName(args.takeFirst());
dns->setType(query.type);
if (!query.nameServer.isNull())
dns->setNameserver(query.nameServer);
dns->setName(query.name);
dns->lookup();
}
@ -159,7 +202,33 @@ int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
//! [1]
QCoreApplication::setApplicationVersion(QT_VERSION_STR);
QCoreApplication::setApplicationName(QCoreApplication::translate("QDnsLookupExample", "DNS Lookup Example"));
QCommandLineParser parser;
parser.setApplicationDescription(QCoreApplication::translate("QDnsLookupExample", "An example demonstrating the class QDnsLookup."));
DnsQuery query;
QString errorMessage;
switch (parseCommandLine(parser, &query, &errorMessage)) {
case CommandLineOk:
break;
case CommandLineError:
fputs(qPrintable(errorMessage), stderr);
fputs("\n\n", stderr);
fputs(qPrintable(parser.helpText()), stderr);
return 1;
case CommandLineVersionRequested:
printf("%s %s\n", qPrintable(QCoreApplication::applicationName()),
qPrintable(QCoreApplication::applicationVersion()));
return 0;
case CommandLineHelpRequested:
parser.showHelp();
Q_UNREACHABLE();
}
//! [1]
DnsManager manager;
manager.setQuery(query);
QTimer::singleShot(0, &manager, SLOT(execute()));
return app.exec();

View File

@ -38,11 +38,21 @@
**
****************************************************************************/
#include <QObject>
#include <QDnsLookup>
#include <QHostAddress>
QT_BEGIN_NAMESPACE
class QDnsLookup;
QT_END_NAMESPACE
//! [0]
struct DnsQuery
{
DnsQuery() : type(QDnsLookup::A) {}
QDnsLookup::Type type;
QHostAddress nameServer;
QString name;
};
//! [0]
class DnsManager : public QObject
{
@ -50,6 +60,7 @@ class DnsManager : public QObject
public:
DnsManager();
void setQuery(const DnsQuery &q) { query = q; }
public slots:
void execute();
@ -57,5 +68,6 @@ public slots:
private:
QDnsLookup *dns;
DnsQuery query;
};

View File

@ -37,7 +37,8 @@ exampledirs += \
snippets \
../../../examples/threads/ \
../../../examples/tools/ \
../../../examples/json/
../../../examples/json/ \
../../../examples/network/dnslookup
imagedirs += images

View File

@ -187,6 +187,78 @@ QStringList QCommandLineParserPrivate::aliases(const QString &optionName) const
QCoreApplication::arguments() before QCommandLineParser defines the \c{profile}
option and parses the command line.
\section2 How to Use QCommandLineParser in Complex Applications
In practice, additional error checking needs to be performed on the positional
arguments and option values. For example, ranges of numbers should be checked.
It is then advisable to introduce a function to do the command line parsing
which takes a struct or class receiving the option values returning an
enumeration representing the result. The dnslookup example of the QtNetwork
module illustrates this:
\snippet dnslookup.h 0
\snippet dnslookup.cpp 0
In the main function, help should be printed to the standard output if the help option
was passed and the application should return the exit code 0.
If an error was detected, the error message should be printed to the standard
error output and the application should return an exit code other than 0.
\snippet dnslookup.cpp 1
A special case to consider here are GUI applications on Windows and mobile
platforms. These applications may not use the standard output or error channels
since the output is either discarded or not accessible.
For such GUI applications, it is recommended to display help texts and error messages
using a QMessageBox. To preserve the formatting of the help text, rich text
with \c <pre> elements should be used:
\code
switch (parseCommandLine(parser, &query, &errorMessage)) {
case CommandLineOk:
break;
case CommandLineError:
#ifdef Q_OS_WIN
QMessageBox::warning(0, QGuiApplication::applicationDisplayName(),
"<html><head/><body><h2>" + errorMessage + "</h2><pre>"
+ parser.helpText() + "</pre></body></html>");
#else
fputs(qPrintable(errorMessage), stderr);
fputs("\n\n", stderr);
fputs(qPrintable(parser.helpText()), stderr);
#endif
return 1;
case CommandLineVersionRequested:
#ifdef Q_OS_WIN
QMessageBox::information(0, QGuiApplication::applicationDisplayName(),
QGuiApplication::applicationDisplayName() + ' '
+ QCoreApplication::applicationVersion());
#else
printf("%s %s\n", QGuiApplication::applicationDisplayName(),
qPrintable(QCoreApplication::applicationVersion()));
#endif
return 0;
case CommandLineHelpRequested:
#ifdef Q_OS_WIN
QMessageBox::warning(0, QGuiApplication::applicationDisplayName(),
"<html><head/><body><pre>"
+ parser.helpText() + "</pre></body></html>");
return 0;
#else
parser.showHelp();
Q_UNREACHABLE();
#endif
}
\endcode
However, this does not apply to the dnslookup example, because it is a
console application.
\sa QCommandLineOption, QCoreApplication
*/