目录
简介
一般源码分析都是从main开始的,此次自然也不例外。我们来分析/Cockatrice/cockatrice/src/main.cpp。main()中主要实现了Qt应用程序的标准流程,并初始化了一些程序模块。我们也得以管中窥豹,从中初步了解Cockatrice的模块划分与设计思路。
源码分析
主要模块如下所示:
[1]Qt应用程序标准框架
标准的Qt GUI应用程序写法,很传统但也很经典。main中主要实现了界面参数初始化,资源加载与功能模块实例化。
QSystemTrayIcon *trayIcon;
int main(int argc, char *argv[])
{
QApplication app(argc, argv);---->在栈上创建QApplication实例
QObject::connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit()));---->绑定lastWindowClosed()信号,保证最后一个窗口关闭时触发quit()最终退出程序
qInstallMessageHandler(CockatriceLogger);---->注册日志处理函数
#ifdef Q_OS_WIN
app.addLibraryPath(app.applicationDirPath() + "/plugins");---->添加指定的函数库加载路径
#endif
// These values are only used by the settings loader/saver
// Wrong or outdated values are kept to not break things
QCoreApplication::setOrganizationName("Cockatrice");---->设置组织,公司,应用,版本名称
QCoreApplication::setOrganizationDomain("cockatrice.de");
QCoreApplication::setApplicationName("Cockatrice");
QCoreApplication::setApplicationVersion(VERSION_STRING);
#ifdef Q_OS_MAC
qApp->setAttribute(Qt::AA_DontShowIconsInMenus, true);---->设置是否在菜单中添加本应用
#endif
。。。
QCommandLineParser parser;---->用QCommandLineParser来设置并处理用户传参
parser.setApplicationDescription("Cockatrice");---->设置应用描述
parser.addHelpOption();---->添加帮助选项("-h" 或 "--help")
parser.addVersionOption();---->添加版本选项("-v" 或 "--version")
parser.addOptions(
{{{"c", "connect"}, QCoreApplication::translate("main", "Connect on startup"), "user:pass@host:port"},
{{"d", "debug-output"}, QCoreApplication::translate("main", "Debug to file")}});---->添加用户自定义选项
parser.process(app);---->将以上所有解析选项添加到QApplication实例
if (parser.isSet("debug-output")) {
Logger::getInstance().logToFile(true);---->如果用户输入-d则启用日志
}
。。。
QLocale::setDefault(QLocale::English);---->设置默认语言环境为English
qsrand(QDateTime::currentDateTime().toTime_t());---->调用系统当前时间生成随机数种子
qDebug("main(): starting main program");
MainWindow ui;---->在栈上创建MainWindow实例
if (parser.isSet("connect")) {
ui.setConnectTo(parser.value("connect"));---->如果用户输入-c参数,将该参数传入MainWindow实例
}
qDebug("main(): MainWindow constructor finished");
ui.setWindowIcon(QPixmap("theme:cockatrice"));---->设置应用程序左上角显示的图标
。。。
ui.show();---->显示MainWindow
qDebug("main(): ui.show() finished");
app.setAttribute(Qt::AA_UseHighDpiPixmaps);---->启动针对高分辨率Retina的显示支持
app.exec();---->运行QApplication实例,开启事件循环,程序阻塞在此处直到quit()
qDebug("Event loop finished, terminating...");
。。。
PingPixmapGenerator::clear();---->
CountryPixmapGenerator::clear();---->
UserLevelPixmapGenerator::clear();---->
return 0;
}
[2]日志管理模块
调用了Qt提供的logger handle来实现日志功能。
后续单独分析Qt的日志管理。
static void CockatriceLogger(QtMsgType type, const QMessageLogContext &ctx, const QString &message)
{
Logger::getInstance().log(type, ctx, message);
}
int main(int argc, char *argv[])
{
。。。
qInstallMessageHandler(CockatriceLogger);
。。。
}
[3]配置管理模块
SettingCache作为一个独立的功能模块,后续单独分析。
这里用设备MAC地址生成了hash摘要作为用户ID,也是很经典的写法。
SettingsCache *settingsCache;
QString const generateClientID()
{
QString macList;
foreach (QNetworkInterface interface, QNetworkInterface::allInterfaces()) {
if (interface.hardwareAddress() != "")
if (interface.hardwareAddress() != "00:00:00:00:00:00:00:E0")
macList += interface.hardwareAddress() + ".";
}
QString strClientID = QCryptographicHash::hash(macList.toUtf8(), QCryptographicHash::Sha1).toHex().right(15);
return strClientID;
}
int main(int argc, char *argv[])
{
。。。
settingsCache = new SettingsCache;
。。。
settingsCache->setClientID(generateClientID());
。。。
app.exec();
。。。
delete settingsCache;
。。。
}
[4]主题管理模块
ThemeManager作为一个独立的功能模块,后续单独分析。
ThemeManager *themeManager;
int main(int argc, char *argv[])
{
。。。
themeManager = new ThemeManager;
。。。
app.exec();
。。。
delete themeManager;
。。。
}
[5]音频引擎模块
SoundEngine作为一个独立的功能模块,后续单独分析。
SoundEngine *soundEngine;
int main(int argc, char *argv[])
{
。。。
soundEngine = new SoundEngine;
。。。
app.exec();
。。。
delete soundEngine;
。。。
}
[6]数据库引擎模块
CardDatabase作为一个独立的功能模块,后续单独分析。
SpoilerBackgroundUpdater用来干什么呢?后续再分析。
CardDatabase *db;
int main(int argc, char *argv[])
{
。。。
db = new CardDatabase;
。。。
// If spoiler mode is enabled, we will download the spoilers
// then reload the DB. otherwise just reload the DB
SpoilerBackgroundUpdater spoilerBackgroundUpdater;
。。。
app.exec();
。。。
delete db;
。。。
}
[7]翻译(Qt国际化)模块
调用了Qt提供的QTranslator来实现日志功能。
后续单独分析Qt国际化。
QTranslator *translator, *qtTranslator;
const QString translationPrefix = "cockatrice";
QString translationPath;
void installNewTranslator()
{
QString lang = settingsCache->getLang();
qtTranslator->load("qt_" + lang, QLibraryInfo::location(QLibraryInfo::TranslationsPath));
qApp->installTranslator(qtTranslator);
translator->load(translationPrefix + "_" + lang, translationPath);
qApp->installTranslator(translator);
qDebug() << "Language changed:" << lang;
}
int main(int argc, char *argv[])
{
。。。
#ifdef Q_OS_MAC
translationPath = qApp->applicationDirPath() + "/../Resources/translations";---->设置翻译文件加载路径
#elif defined(Q_OS_WIN)
translationPath = qApp->applicationDirPath() + "/translations";
#else // linux
translationPath = qApp->applicationDirPath() + "/../share/cockatrice/translations";
#endif
。。。
qtTranslator = new QTranslator;
translator = new QTranslator;
installNewTranslator();
。。。
app.exec();
。。。
delete qtTranslator;
delete translator;
。。。
}
[8]RNG_SFMT模块(不知道干啥的,以后慢慢分析)
RNG_Abstract *rng;
int main(int argc, char *argv[])
{
。。。
rng = new RNG_SFMT;
。。。
app.exec();
。。。
delete rng;
。。。
}
下一篇我们来分析Cockatrice的Client端界面结构。