目录
1 Application::runApplication()源码
2 Application::runApplication()注释
此代码为FreeCAD-main\FreeCAD-main\src\Gui下面的Application.cpp的内容
void Application::runApplication()
{
const std::map& cfg = App::Application::Config();
std::map::const_iterator it;
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
#endif
// Automatic scaling for legacy apps (disable once all parts of GUI are aware of HiDpi)
ParameterGrp::handle hDPI =
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/HighDPI");
bool disableDpiScaling = hDPI->GetBool("DisableDpiScaling", false);
if (disableDpiScaling) {
#ifdef FC_OS_WIN32
SetProcessDPIAware(); // call before the main event loop
#endif
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
QApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
#endif
}
else {
// Enable automatic scaling based on pixel density of display (added in Qt 5.6)
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
#if QT_VERSION >= QT_VERSION_CHECK(5,14,0) && defined(Q_OS_WIN)
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
}
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
//Enable support for highres images (added in Qt 5.1, but off by default)
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
// Use software rendering for OpenGL
ParameterGrp::handle hOpenGL =
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/OpenGL");
bool useSoftwareOpenGL = hOpenGL->GetBool("UseSoftwareOpenGL", false);
if (useSoftwareOpenGL) {
QApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
// By default (on platforms that support it, see docs for
// Qt::AA_CompressHighFrequencyEvents) QT applies compression
// for high frequency events (mouse move, touch, window resizes)
// to keep things smooth even when handling the event takes a
// while (e.g. to calculate snapping).
// However, tablet pen move events (and mouse move events
// synthesised from those) are not compressed by default (to
// allow maximum precision when e.g. hand-drawing curves),
// leading to unacceptable slowdowns using a tablet pen. Enable
// compression for tablet events here to solve that.
QCoreApplication::setAttribute(Qt::AA_CompressTabletEvents);
#endif
// A new QApplication
Base::Console().Log("Init: Creating Gui::Application and QApplication\n");
// if application not yet created by the splasher
int argc = App::Application::GetARGC();
GUISingleApplication mainApp(argc, App::Application::GetARGV());
// https://forum.freecad.org/viewtopic.php?f=3&t=15540
mainApp.setAttribute(Qt::AA_DontShowIconsInMenus, false);
// Make sure that we use '.' as decimal point. See also
// http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=559846
// and issue #0002891
// http://doc.qt.io/qt-5/qcoreapplication.html#locale-settings
setlocale(LC_NUMERIC, "C");
// check if a single or multiple instances can run
it = cfg.find("SingleInstance");
if (it != cfg.end() && mainApp.isRunning()) {
// send the file names to be opened to the server application so that this
// opens them
QDir cwd = QDir::current();
std::list files = App::Application::getCmdLineFiles();
for (const auto & file : files) {
QString fn = QString::fromUtf8(file.c_str(), static_cast(file.size()));
QFileInfo fi(fn);
// if path name is relative make it absolute because the running instance
// cannot determine the full path when trying to load the file
if (fi.isRelative()) {
fn = cwd.absoluteFilePath(fn);
fn = QDir::cleanPath(fn);
}
QByteArray msg = fn.toUtf8();
msg.prepend("OpenFile:");
if (!mainApp.sendMessage(msg)) {
qWarning("Failed to send message to server");
break;
}
}
return;
}
// set application icon and window title
it = cfg.find("Application");
if (it != cfg.end()) {
mainApp.setApplicationName(QString::fromUtf8(it->second.c_str()));
}
else {
mainApp.setApplicationName(QString::fromStdString(App::Application::getExecutableName()));
}
#ifndef Q_OS_MACX
mainApp.setWindowIcon(
Gui::BitmapFactory().pixmap(App::Application::Config()["AppIcon"].c_str()));
#endif
QString plugin;
plugin = QString::fromStdString(App::Application::getHomePath());
plugin += QLatin1String("/plugins");
QCoreApplication::addLibraryPath(plugin);
// setup the search paths for Qt style sheets
QStringList qssPaths;
qssPaths << QString::fromUtf8(
(App::Application::getUserAppDataDir() + "Gui/Stylesheets/").c_str())
<< QString::fromUtf8((App::Application::getResourceDir() + "Gui/Stylesheets/").c_str())
<< QLatin1String(":/stylesheets");
QDir::setSearchPaths(QString::fromLatin1("qss"), qssPaths);
// setup the search paths for Qt overlay style sheets
QStringList qssOverlayPaths;
qssOverlayPaths << QString::fromUtf8((App::Application::getUserAppDataDir()
+ "Gui/Stylesheets/overlay").c_str())
<< QString::fromUtf8((App::Application::getResourceDir()
+ "Gui/Stylesheets/overlay").c_str());
QDir::setSearchPaths(QStringLiteral("overlay"), qssOverlayPaths);
// set search paths for images
QStringList imagePaths;
imagePaths << QString::fromUtf8((App::Application::getUserAppDataDir() + "Gui/images").c_str())
<< QString::fromUtf8((App::Application::getUserAppDataDir() + "pixmaps").c_str())
<< QLatin1String(":/icons");
QDir::setSearchPaths(QString::fromLatin1("images"), imagePaths);
// register action style event type
ActionStyleEvent::EventType = QEvent::registerEventType(QEvent::User + 1);
ParameterGrp::handle hTheme = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Bitmaps/Theme");
#if !defined(Q_OS_LINUX)
QIcon::setThemeSearchPaths(QIcon::themeSearchPaths()
<< QString::fromLatin1(":/icons/FreeCAD-default"));
QIcon::setThemeName(QLatin1String("FreeCAD-default"));
#else
// Option to opt-out from using a Linux desktop icon theme.
// https://forum.freecad.org/viewtopic.php?f=4&t=35624
bool themePaths = hTheme->GetBool("ThemeSearchPaths",true);
if (!themePaths) {
QStringList searchPaths;
searchPaths.prepend(QString::fromUtf8(":/icons"));
QIcon::setThemeSearchPaths(searchPaths);
QIcon::setThemeName(QLatin1String("FreeCAD-default"));
}
#endif
std::string searchpath = hTheme->GetASCII("SearchPath");
if (!searchpath.empty()) {
QStringList searchPaths = QIcon::themeSearchPaths();
searchPaths.prepend(QString::fromUtf8(searchpath.c_str()));
QIcon::setThemeSearchPaths(searchPaths);
}
std::string name = hTheme->GetASCII("Name");
if (!name.empty()) {
QIcon::setThemeName(QString::fromLatin1(name.c_str()));
}
#if defined(FC_OS_LINUX)
// See #0001588
QString path = FileDialog::restoreLocation();
FileDialog::setWorkingDirectory(QDir::currentPath());
FileDialog::saveLocation(path);
#else
FileDialog::setWorkingDirectory(FileDialog::restoreLocation());
#endif
Application app(true);
MainWindow mw;
mw.setProperty("QuitOnClosed", true);
// allow to disable version number
ParameterGrp::handle hGen =
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General");
bool showVersion = hGen->GetBool("ShowVersionInTitle", true);
if (showVersion) {
// set main window title with FreeCAD Version
std::map& config = App::Application::Config();
QString major = QString::fromLatin1(config["BuildVersionMajor"].c_str());
QString minor = QString::fromLatin1(config["BuildVersionMinor"].c_str());
QString point = QString::fromLatin1(config["BuildVersionPoint"].c_str());
QString suffix = QString::fromLatin1(config["BuildVersionSuffix"].c_str());
QString title =
QString::fromLatin1("%1 %2.%3.%4%5").arg(mainApp.applicationName(), major, minor, point, suffix);
mw.setWindowTitle(title);
}
else {
mw.setWindowTitle(mainApp.applicationName());
}
QObject::connect(&mainApp, SIGNAL(messageReceived(const QList &)),
&mw, SLOT(processMessages(const QList &)));
ParameterGrp::handle hDocGrp = WindowParameter::getDefaultParameter()->GetGroup("Document");
int timeout = hDocGrp->GetInt("AutoSaveTimeout", 15); // 15 min
if (!hDocGrp->GetBool("AutoSaveEnabled", true))
timeout = 0;
AutoSaver::instance()->setTimeout(timeout * 60000);
AutoSaver::instance()->setCompressed(hDocGrp->GetBool("AutoSaveCompressed", true));
// set toolbar icon size
ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("General");
int size = hGrp->GetInt("ToolbarIconSize", 0);
if (size >= 16) // must not be lower than this
mw.setIconSize(QSize(size,size));
// filter wheel events for combo boxes
if (hGrp->GetBool("ComboBoxWheelEventFilter", false)) {
auto filter = new WheelEventFilter(&mainApp);
mainApp.installEventFilter(filter);
}
// For values different to 1 and 2 use the OS locale settings
auto localeFormat = hGrp->GetInt("UseLocaleFormatting", 0);
if (localeFormat == 1) {
Translator::instance()->setLocale(
hGrp->GetASCII("Language", Translator::instance()->activeLanguage().c_str()));
}
else if (localeFormat == 2) {
Translator::instance()->setLocale("C");
}
// set text cursor blinking state
int blinkTime = hGrp->GetBool("EnableCursorBlinking", true) ? -1 : 0;
qApp->setCursorFlashTime(blinkTime);
{
QWindow window;
window.setSurfaceType(QWindow::OpenGLSurface);
window.create();
QOpenGLContext context;
if (context.create()) {
context.makeCurrent(&window);
if (!context.functions()->hasOpenGLFeature(QOpenGLFunctions::Framebuffers)) {
Base::Console().Log("This system does not support framebuffer objects\n");
}
if (!context.functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)) {
Base::Console().Log("This system does not support NPOT textures\n");
}
int major = context.format().majorVersion();
int minor = context.format().minorVersion();
#ifdef NDEBUG
// In release mode, issue a warning to users that their version of OpenGL is
// potentially going to cause problems
if (major < 2) {
auto message =
QObject::tr("This system is running OpenGL %1.%2. "
"FreeCAD requires OpenGL 2.0 or above. "
"Please upgrade your graphics driver and/or card as required.")
.arg(major)
.arg(minor)
+ QStringLiteral("\n");
Base::Console().Warning(message.toStdString().c_str());
Dialog::DlgCheckableMessageBox::showMessage(
Gui::GUISingleApplication::applicationName() + QStringLiteral(" - ")
+ QObject::tr("Invalid OpenGL Version"),
message);
}
#endif
const char* glVersion = reinterpret_cast(glGetString(GL_VERSION));
Base::Console().Log("OpenGL version is: %d.%d (%s)\n", major, minor, glVersion);
}
}
// init the Inventor subsystem
initOpenInventor();
QString home = QString::fromStdString(App::Application::getHomePath());
it = cfg.find("WindowTitle");
if (it != cfg.end()) {
QString title = QString::fromUtf8(it->second.c_str());
mw.setWindowTitle(title);
}
it = cfg.find("WindowIcon");
if (it != cfg.end()) {
QString path = QString::fromUtf8(it->second.c_str());
if (QDir(path).isRelative()) {
path = QFileInfo(QDir(home), path).absoluteFilePath();
}
QApplication::setWindowIcon(QIcon(path));
}
it = cfg.find("ProgramLogo");
if (it != cfg.end()) {
QString path = QString::fromUtf8(it->second.c_str());
if (QDir(path).isRelative()) {
path = QFileInfo(QDir(home), path).absoluteFilePath();
}
QPixmap px(path);
if (!px.isNull()) {
auto logo = new QLabel();
logo->setPixmap(px.scaledToHeight(32));
mw.statusBar()->addPermanentWidget(logo, 0);
logo->setFrameShape(QFrame::NoFrame);
}
}
bool hidden = false;
it = cfg.find("StartHidden");
if (it != cfg.end()) {
hidden = true;
}
// show splasher while initializing the GUI
if (!hidden)
mw.startSplasher();
// running the GUI init script
try {
Base::Console().Log("Run Gui init script\n");
runInitGuiScript();
setImportImageFormats();
}
catch (const Base::Exception& e) {
Base::Console().Error("Error in FreeCADGuiInit.py: %s\n", e.what());
mw.stopSplasher();
throw;
}
// stop splash screen and set immediately the active window that may be of interest
// for scripts using Python binding for Qt
mw.stopSplasher();
mainApp.setActiveWindow(&mw);
// Activate the correct workbench
std::string start = App::Application::Config()["StartWorkbench"];
Base::Console().Log("Init: Activating default workbench %s\n", start.c_str());
std::string autoload =
App::GetApplication()
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
->GetASCII("AutoloadModule", start.c_str());
if ("$LastModule" == autoload) {
start = App::GetApplication()
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
->GetASCII("LastModule", start.c_str());
}
else {
start = autoload;
}
// if the auto workbench is not visible then force to use the default workbech
// and replace the wrong entry in the parameters
QStringList wb = app.workbenches();
if (!wb.contains(QString::fromLatin1(start.c_str()))) {
start = App::Application::Config()["StartWorkbench"];
if ("$LastModule" == autoload) {
App::GetApplication()
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
->SetASCII("LastModule", start.c_str());
}
else {
App::GetApplication()
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
->SetASCII("AutoloadModule", start.c_str());
}
}
// Call this before showing the main window because otherwise:
// 1. it shows a white window for a few seconds which doesn't look nice
// 2. the layout of the toolbars is completely broken
app.activateWorkbench(start.c_str());
// show the main window
if (!hidden) {
Base::Console().Log("Init: Showing main window\n");
mw.loadWindowSettings();
}
hGrp = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/MainWindow");
std::string style = hGrp->GetASCII("StyleSheet");
if (style.empty()) {
// check the branding settings
const auto& config = App::Application::Config();
auto it = config.find("StyleSheet");
if (it != config.end())
style = it->second;
}
app.setStyleSheet(QLatin1String(style.c_str()), hGrp->GetBool("TiledBackground", false));
//initialize spaceball.
mainApp.initSpaceball(&mw);
#ifdef FC_DEBUG // redirect Coin messages to FreeCAD
SoDebugError::setHandlerCallback( messageHandlerCoin, 0 );
#endif
// Now run the background autoload, for workbenches that should be loaded at startup, but not
// displayed to the user immediately
std::string autoloadCSV =
App::GetApplication()
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
->GetASCII("BackgroundAutoloadModules", "");
// Tokenize the comma-separated list and load the requested workbenches if they exist in this
// installation
std::vector backgroundAutoloadedModules;
std::stringstream stream(autoloadCSV);
std::string workbench;
while (std::getline(stream, workbench, ',')) {
if (wb.contains(QString::fromLatin1(workbench.c_str())))
app.activateWorkbench(workbench.c_str());
}
// Reactivate the startup workbench
app.activateWorkbench(start.c_str());
Instance->d->startingUp = false;
// gets called once we start the event loop
QTimer::singleShot(0, &mw, SLOT(delayedStartup()));
// run the Application event loop
Base::Console().Log("Init: Entering event loop\n");
// boot phase reference point
// https://forum.freecad.org/viewtopic.php?f=10&t=21665
Gui::getMainWindow()->setProperty("eventLoop", true);
try {
std::stringstream s;
s << App::Application::getUserCachePath() << App::Application::getExecutableName()
<< "_" << QCoreApplication::applicationPid() << ".lock";
// open a lock file with the PID
Base::FileInfo fi(s.str());
Base::ofstream lock(fi);
// In case the file_lock cannot be created start FreeCAD without IPC support.
#if !defined(FC_OS_WIN32) || (BOOST_VERSION < 107600)
std::string filename = s.str();
#else
std::wstring filename = fi.toStdWString();
#endif
std::unique_ptr flock;
try {
flock = std::make_unique(filename.c_str());
flock->lock();
}
catch (const boost::interprocess::interprocess_exception& e) {
QString msg = QString::fromLocal8Bit(e.what());
Base::Console().Warning("Failed to create a file lock for the IPC: %s\n",
msg.toUtf8().constData());
}
Base::Console().Log("Init: Executing event loop...\n");
mainApp.exec();
// Qt can't handle exceptions thrown from event handlers, so we need
// to manually rethrow SystemExitExceptions.
if (mainApp.caughtException.get())
throw Base::SystemExitException(*mainApp.caughtException.get());
// close the lock file, in case of a crash we can see the existing lock file
// on the next restart and try to repair the documents, if needed.
if (flock.get())
flock->unlock();
lock.close();
fi.deleteFile();
}
catch (const Base::SystemExitException&) {
Base::Console().Message("System exit\n");
throw;
}
catch (const std::exception& e) {
// catching nasty stuff coming out of the event loop
Base::Console().Error("Event loop left through unhandled exception: %s\n", e.what());
App::Application::destructObserver();
throw;
}
catch (...) {
// catching nasty stuff coming out of the event loop
Base::Console().Error("Event loop left through unknown unhandled exception\n");
App::Application::destructObserver();
throw;
}
Base::Console().Log("Finish: Event loop left\n");
}
void Application::runApplication()
{
// 获取应用程序的配置信息
const std::map& cfg = App::Application::Config();
// 定义一个常量迭代器,用于遍历配置信息
std::map::const_iterator it;
// 设置 Qt 应用程序的属性,确保共享 OpenGL 上下文
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
// 如果 Qt 版本大于或等于 5.12.0,则设置使用桌面 OpenGL
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
#endif
// 获取高 DPI 参数设置
ParameterGrp::handle hDPI =
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/HighDPI");
// 判断是否禁用 DPI 缩放
bool disableDpiScaling = hDPI->GetBool("DisableDpiScaling", false);
if (disableDpiScaling) {
// 如果禁用了 DPI 缩放
#ifdef FC_OS_WIN32
// 在主事件循环之前设置 Windows DPI 感知
SetProcessDPIAware();
#endif
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
// 如果 Qt 版本小于 6.0.0,禁用高 DPI 缩放
QApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
#endif
}
else {
// 如果启用了 DPI 缩放
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
// 如果 Qt 版本小于 6.0.0,启用高 DPI 缩放
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
#if QT_VERSION >= QT_VERSION_CHECK(5,14,0) && defined(Q_OS_WIN)
// 如果 Qt 版本大于或等于 5.14.0 且在 Windows 上,设置高 DPI 比例舍入策略为 PassThrough
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
}
}
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
//Enable support for highres images (added in Qt 5.1, but off by default)
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
// Use software rendering for OpenGL
ParameterGrp::handle hOpenGL =
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/OpenGL");
bool useSoftwareOpenGL = hOpenGL->GetBool("UseSoftwareOpenGL", false);
if (useSoftwareOpenGL) {
QApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
// By default (on platforms that support it, see docs for
// Qt::AA_CompressHighFrequencyEvents) QT applies compression
// for high frequency events (mouse move, touch, window resizes)
// to keep things smooth even when handling the event takes a
// while (e.g. to calculate snapping).
// However, tablet pen move events (and mouse move events
// synthesised from those) are not compressed by default (to
// allow maximum precision when e.g. hand-drawing curves),
// leading to unacceptable slowdowns using a tablet pen. Enable
// compression for tablet events here to solve that.
QCoreApplication::setAttribute(Qt::AA_CompressTabletEvents);
#endif
// A new QApplication
Base::Console().Log("Init: Creating Gui::Application and QApplication\n");
// 如果没有由Splasher创建应用程序
int argc = App::Application::GetARGC(); // 获取应用程序的参数数量
GUISingleApplication mainApp(argc, App::Application::GetARGV()); // 创建主应用程序实例
// 禁止在菜单中显示图标
mainApp.setAttribute(Qt::AA_DontShowIconsInMenus, false);
// 设置使用'.'作为小数点。参见
// http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=559846
// 以及 issue #0002891
// http://doc.qt.io/qt-5/qcoreapplication.html#locale-settings
setlocale(LC_NUMERIC, "C"); // 设置区域设置以使用'.'作为小数点
// 检查是否可以运行单个或多个实例
it = cfg.find("SingleInstance"); // 在配置中查找"SingleInstance"键
if (it != cfg.end() && mainApp.isRunning()) { // 如果找到并且应用程序正在运行
// 向服务器应用程序发送要打开的文件名,以便此应用程序打开它们
QDir cwd = QDir::current(); // 获取当前工作目录
std::list files = App::Application::getCmdLineFiles(); // 获取命令行文件列表
for (const auto & file : files) { // 遍历文件列表
QString fn = QString::fromUtf8(file.c_str(), static_cast(file.size())); // 将文件名转换为QString
QFileInfo fi(fn); // 获取文件信息
// 如果路径名是相对的,则使其成为绝对路径,因为正在运行的应用程序
// 在尝试加载文件时无法确定完整路径
if (fi.isRelative()) {
fn = cwd.absoluteFilePath(fn); // 获取绝对路径
fn = QDir::cleanPath(fn); // 清理路径中的冗余字符
}
QByteArray msg = fn.toUtf8(); // 将文件名转换为字节数组
msg.prepend("OpenFile:"); // 在消息前添加前缀
if (!mainApp.sendMessage(msg)) { // 如果消息发送失败
qWarning("Failed to send message to server"); // 发出警告
break; // 退出循环
}
}
return; // 结束函数执行
}
// 从配置文件中查找"Application"键,如果找到,就将其值设置为应用程序的名称。
// 如果没有找到,就将应用程序的名称设置为可执行文件的名称。
it = cfg.find("Application");
if (it != cfg.end()) {
mainApp.setApplicationName(QString::fromUtf8(it->second.c_str()));
}
else {
mainApp.setApplicationName(QString::fromStdString(App::Application::getExecutableName()));
}
// 如果不是在Mac OS X系统上运行,就设置应用程序的窗口图标。
#ifndef Q_OS_MACX
mainApp.setWindowIcon(
Gui::BitmapFactory().pixmap(App::Application::Config()["AppIcon"].c_str()));
#endif
// 设置插件库的路径,路径为应用程序的主目录加上"/plugins"。
QString plugin;
plugin = QString::fromStdString(App::Application::getHomePath());
plugin += QLatin1String("/plugins");
QCoreApplication::addLibraryPath(plugin);
// 设置Qt样式表的搜索路径,包括用户数据目录下的样式表、资源目录下的样式表和内置的样式表。
QStringList qssPaths;
qssPaths << QString::fromUtf8(
(App::Application::getUserAppDataDir() + "Gui/Stylesheets/").c_str())
<< QString::fromUtf8((App::Application::getResourceDir() + "Gui/Stylesheets/").c_str())
<< QLatin1String(":/stylesheets");
QDir::setSearchPaths(QString::fromLatin1("qss"), qssPaths);
// 设置图像的搜索路径,包括用户数据目录下的图像、用户数据目录下的像素图和内置的图标。
QStringList imagePaths;
imagePaths << QString::fromUtf8((App::Application::getUserAppDataDir() + "Gui/images").c_str())
<< QString::fromUtf8((App::Application::getUserAppDataDir() + "pixmaps").c_str())
<< QLatin1String(":/icons");
QDir::setSearchPaths(QString::fromLatin1("images"), imagePaths);
// 注册动作样式事件类型。
ActionStyleEvent::EventType = QEvent::registerEventType(QEvent::User + 1);
// 获取用户参数中的主题设置。
ParameterGrp::handle hTheme = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Bitmaps/Theme");
// 如果不是在Linux系统上运行,就设置图标主题搜索路径和图标主题名称。
#if !defined(Q_OS_LINUX)
QIcon::setThemeSearchPaths(QIcon::themeSearchPaths()
<< QString::fromLatin1(":/icons/FreeCAD-default"));
QIcon::setThemeName(QLatin1String("FreeCAD-default"));
#else
// 获取用户参数中"ThemeSearchPaths"键的值,如果值为true,则不需要进一步操作;否则,将搜索路径设置为:":/icons"
bool themePaths = hTheme->GetBool("ThemeSearchPaths",true);
if (!themePaths) {
QStringList searchPaths;
searchPaths.prepend(QString::fromUtf8(":/icons"));
QIcon::setThemeSearchPaths(searchPaths);
QIcon::setThemeName(QLatin1String("FreeCAD-default"));
}
#endif
// 获取用户参数中"SearchPath"键的值,如果该值不为空,则将其添加到图标主题的搜索路径中
std::string searchpath = hTheme->GetASCII("SearchPath");
if (!searchpath.empty()) {
QStringList searchPaths = QIcon::themeSearchPaths();
searchPaths.prepend(QString::fromUtf8(searchpath.c_str()));
QIcon::setThemeSearchPaths(searchPaths);
}
// 获取用户参数中"Name"键的值,如果该值不为空,则将其设置为图标主题的名称
std::string name = hTheme->GetASCII("Name");
if (!name.empty()) {
QIcon::setThemeName(QString::fromLatin1(name.c_str()));
}
#if defined(FC_OS_LINUX)
// 这是为了解决特定于Linux的bug #0001588。在Linux上,恢复文件对话框的工作目录并保存当前的工作目录。
// 注释中的"See #0001588"指的是这个bug的编号。
QString path = FileDialog::restoreLocation();
FileDialog::setWorkingDirectory(QDir::currentPath());
FileDialog::saveLocation(path);
#else
// 在非Linux系统上,恢复文件对话框的工作目录。
FileDialog::setWorkingDirectory(FileDialog::restoreLocation());
#endif
Application app(true);
MainWindow mw;
mw.setProperty("QuitOnClosed", true);
// allow to disable version number
ParameterGrp::handle hGen =
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General");
bool showVersion = hGen->GetBool("ShowVersionInTitle", true);
if (showVersion) {
// set main window title with FreeCAD Version
std::map& config = App::Application::Config();
QString major = QString::fromLatin1(config["BuildVersionMajor"].c_str());
QString minor = QString::fromLatin1(config["BuildVersionMinor"].c_str());
QString point = QString::fromLatin1(config["BuildVersionPoint"].c_str());
QString suffix = QString::fromLatin1(config["BuildVersionSuffix"].c_str());
QString title =
QString::fromLatin1("%1 %2.%3.%4%5").arg(mainApp.applicationName(), major, minor, point, suffix);
mw.setWindowTitle(title);
}
else {
mw.setWindowTitle(mainApp.applicationName());
}
QObject::connect(&mainApp, SIGNAL(messageReceived(const QList &)),
&mw, SLOT(processMessages(const QList &)));
ParameterGrp::handle hDocGrp = WindowParameter::getDefaultParameter()->GetGroup("Document");
int timeout = hDocGrp->GetInt("AutoSaveTimeout", 15); // 15 min
if (!hDocGrp->GetBool("AutoSaveEnabled", true))
timeout = 0;
AutoSaver::instance()->setTimeout(timeout * 60000);
AutoSaver::instance()->setCompressed(hDocGrp->GetBool("AutoSaveCompressed", true));
// set toolbar icon size
ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("General");
int size = hGrp->GetInt("ToolbarIconSize", 0);
if (size >= 16) // must not be lower than this
mw.setIconSize(QSize(size,size));
// filter wheel events for combo boxes
if (hGrp->GetBool("ComboBoxWheelEventFilter", false)) {
auto filter = new WheelEventFilter(&mainApp);
mainApp.installEventFilter(filter);
}
// For values different to 1 and 2 use the OS locale settings
auto localeFormat = hGrp->GetInt("UseLocaleFormatting", 0);
if (localeFormat == 1) {
Translator::instance()->setLocale(
hGrp->GetASCII("Language", Translator::instance()->activeLanguage().c_str()));
}
else if (localeFormat == 2) {
Translator::instance()->setLocale("C");
}
// set text cursor blinking state
int blinkTime = hGrp->GetBool("EnableCursorBlinking", true) ? -1 : 0;
qApp->setCursorFlashTime(blinkTime);
{
QWindow window;
window.setSurfaceType(QWindow::OpenGLSurface);
window.create();
QOpenGLContext context;
if (context.create()) {
context.makeCurrent(&window);
if (!context.functions()->hasOpenGLFeature(QOpenGLFunctions::Framebuffers)) {
Base::Console().Log("This system does not support framebuffer objects\n");
}
if (!context.functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)) {
Base::Console().Log("This system does not support NPOT textures\n");
}
int major = context.format().majorVersion();
int minor = context.format().minorVersion();
#ifdef NDEBUG
// In release mode, issue a warning to users that their version of OpenGL is
// potentially going to cause problems
if (major < 2) {
auto message =
QObject::tr("This system is running OpenGL %1.%2. "
"FreeCAD requires OpenGL 2.0 or above. "
"Please upgrade your graphics driver and/or card as required.")
.arg(major)
.arg(minor)
+ QStringLiteral("\n");
Base::Console().Warning(message.toStdString().c_str());
Dialog::DlgCheckableMessageBox::showMessage(
Gui::GUISingleApplication::applicationName() + QStringLiteral(" - ")
+ QObject::tr("Invalid OpenGL Version"),
message);
}
#endif
const char* glVersion = reinterpret_cast(glGetString(GL_VERSION));
Base::Console().Log("OpenGL version is: %d.%d (%s)\n", major, minor, glVersion);
}
}
// init the Inventor subsystem
initOpenInventor();
QString home = QString::fromStdString(App::Application::getHomePath());
it = cfg.find("WindowTitle");
if (it != cfg.end()) {
QString title = QString::fromUtf8(it->second.c_str());
mw.setWindowTitle(title);
}
it = cfg.find("WindowIcon");
if (it != cfg.end()) {
QString path = QString::fromUtf8(it->second.c_str());
if (QDir(path).isRelative()) {
path = QFileInfo(QDir(home), path).absoluteFilePath();
}
QApplication::setWindowIcon(QIcon(path));
}
it = cfg.find("ProgramLogo");
if (it != cfg.end()) {
QString path = QString::fromUtf8(it->second.c_str());
if (QDir(path).isRelative()) {
path = QFileInfo(QDir(home), path).absoluteFilePath();
}
QPixmap px(path);
if (!px.isNull()) {
auto logo = new QLabel();
logo->setPixmap(px.scaledToHeight(32));
mw.statusBar()->addPermanentWidget(logo, 0);
logo->setFrameShape(QFrame::NoFrame);
}
}
bool hidden = false;
it = cfg.find("StartHidden");
if (it != cfg.end()) {
hidden = true;
}
// show splasher while initializing the GUI
if (!hidden)
mw.startSplasher();
// running the GUI init script
try {
Base::Console().Log("Run Gui init script\n");
runInitGuiScript();
setImportImageFormats();
}
catch (const Base::Exception& e) {
Base::Console().Error("Error in FreeCADGuiInit.py: %s\n", e.what());
mw.stopSplasher();
throw;
}
mw.stopSplasher();
mw.stopSplasher();:停止显示启动画面(Splasher)。
mainApp.setActiveWindow(&mw);
mainApp.setActiveWindow(&mw);:将mw窗口设为当前激活窗口。
std::string start = App::Application::Config()["StartWorkbench"];
从配置中获取默认的工作台名称。
Base::Console().Log("Init: Activating default workbench %s\n", start.c_str());
打印日志,显示正在激活默认的工作台。
std::string autoload = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")->GetASCII("AutoloadModule", start.c_str());
从参数组中获取“AutoloadModule”参数的值。
if ("$LastModule" == autoload) {
start = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")->GetASCII("LastModule", start.c_str());
} else {
start = autoload;
}
如果“AutoloadModule”的值是"$LastModule",则获取“LastModule”参数的值,否则直接使用“AutoloadModule”的值。
QStringList wb = app.workbenches();
获取所有可用的工作台列表。
if (!wb.contains(QString::fromLatin1(start.c_str()))) {
start = App::Application::Config()["StartWorkbench"];
if ("$LastModule" == autoload) {
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")->SetASCII("LastModule", start.c_str());
} else {
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")->SetASCII("AutoloadModule", start.c_str());
}
}
如果获取的工作台名称不在列表中,则将默认的工作台名称设为激活的工作台,并相应地更新参数中的“AutoloadModule”或“LastModule”。
这段代码主要是处理CAD软件中工作台的激活和工作台名称的获取与设置。
hGrp = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/MainWindow");
获取“User parameter:BaseApp/Preferences/MainWindow”路径下的参数组,并将其赋值给hGrp。
std::string style = hGrp->GetASCII("StyleSheet");
从hGrp参数组中获取“StyleSheet”参数的值,并将其赋值给style。
if (style.empty()) {
// check the branding settings
const auto& config = App::Application::Config();
auto it = config.find("StyleSheet");
if (it != config.end())
style = it->second;
}
如果style为空,则检查应用程序的配置,查找“StyleSheet”参数的值,并将其赋值给style。
app.setStyleSheet(QLatin1String(style.c_str()), hGrp->GetBool("TiledBackground", false));
使用style设置应用程序的样式表,并根据“TiledBackground”参数的值设置背景。如果“TiledBackground”参数不存在,则使用默认值false。
#ifdef FC_DEBUG // redirect Coin messages to FreeCAD
SoDebugError::setHandlerCallback( messageHandlerCoin, 0 );
#endif
如果定义了FC_DEBUG,则将Coin的消息重定向到FreeCAD。这通常用于调试目的。
// Now run the background autoload, for workbenches that should be loaded at startup, but not
// displayed to the user immediately
std::string autoloadCSV =
App::GetApplication()
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
->GetASCII("BackgroundAutoloadModules", "");
获取“BackgroundAutoloadModules”参数的值,并将其赋值给autoloadCSV。这个参数用于指定在启动时自动加载但不会立即显示给用户的工作台。
// 将逗号分隔的列表分解为多个字符串,并加载当前安装中存在的工作台
std::vector backgroundAutoloadedModules;
创建一个名为backgroundAutoloadedModules的字符串向量,用于存储后台自动加载的工作台。
std::stringstream stream(autoloadCSV);
创建一个std::stringstream对象,用于读取autoloadCSV字符串。
std::string workbench;
声明一个字符串变量workbench,用于存储从列表中读取的工作台名称。
while (std::getline(stream, workbench, ',')) {
if (wb.contains(QString::fromLatin1(workbench.c_str())))
app.activateWorkbench(workbench.c_str());
}
在循环中,从stream中读取以逗号分隔的每个工作台名称。如果工作台存在于wb中,则激活该工作台。
// Reactivate the startup workbench
app.activateWorkbench(start.c_str());
重新激活启动时的工作台。
Instance->d->startingUp = false;
将某个实例的startingUp属性设置为false,表示启动过程已完成。
// gets called once we start the event loop
QTimer::singleShot(0, &mw, SLOT(delayedStartup()));
使用QTimer::singleShot方法在事件循环开始时调用delayedStartup槽。这通常用于延迟执行某些操作。
// run the Application event loop
Base::Console().Log("Init: Entering event loop\n");
开始应用程序的事件循环,并记录日志消息表示已进入事件循环。
// boot phase reference point
// https://forum.freecad.org/viewtopic.php?f=10&t=21665
Gui::getMainWindow()->setProperty("eventLoop", true);
//设置主窗口的属性为“eventLoop”为true,这可能是一个标记,用于跟踪事件循环的状态或进行某些与启动阶段相关的操作。
try {
// 创建一个字符串流,用于构建锁文件的路径
std::stringstream s;
// 连接用户缓存路径、可执行文件名、进程ID和.lock后缀,构建锁文件路径
s << App::Application::getUserCachePath() << App::Application::getExecutableName()
<< "_" << QCoreApplication::applicationPid() << ".lock";
// 使用构建的路径创建一个文件信息对象
Base::FileInfo fi(s.str());
// 创建一个输出文件流,用于写入锁文件
Base::ofstream lock(fi);
// 如果锁文件无法创建,则不启动FreeCAD的IPC支持
#if !defined(FC_OS_WIN32) || (BOOST_VERSION < 107600)
// 获取构建的路径字符串
std::string filename = s.str();
#else
// 获取构建的宽字符路径字符串
std::wstring filename = fi.toStdWString();
#endif
// 创建一个指向boost::interprocess::file_lock的智能指针
std::unique_ptr flock;
try {
// 使用路径创建文件锁对象,并尝试获取锁
flock = std::make_unique(filename.c_str());
flock->lock();
}
catch (const boost::interprocess::interprocess_exception& e) {
// 捕获文件锁异常,并转换为QString进行输出
QString msg = QString::fromLocal8Bit(e.what());
// 输出警告信息,说明IPC的文件锁创建失败
Base::Console().Warning("Failed to create a file lock for the IPC: %s\n",
msg.toUtf8().constData());
}
// 输出日志信息,表示正在执行事件循环
Base::Console().Log("Init: Executing event loop...\n");
// 执行应用程序的事件循环
mainApp.exec();
// Qt在事件处理程序中不能处理异常,因此需要手动重新抛出SystemExitExceptions异常
if (mainApp.caughtException.get())
throw Base::SystemExitException(*mainApp.caughtException.get());
// 如果存在文件锁对象,则解锁文件锁
if (flock.get())
flock->unlock();
// 关闭输出文件流
lock.close();
// 删除锁文件
fi.deleteFile();
}
//这段代码的主要目的是创建并使用一个文件锁来确保同一时间只有一个实例在运行,以避免冲突。在某些操作系统上,还会考虑特定的文件锁的实现细节。在应用程序结束时,会解锁并删除锁文件。
catch (const Base::SystemExitException&) {
Base::Console().Message("System exit\n");
throw;
}
catch (const std::exception& e) {
// catching nasty stuff coming out of the event loop
Base::Console().Error("Event loop left through unhandled exception: %s\n", e.what());
App::Application::destructObserver();
throw;
}
catch (...) {
// catching nasty stuff coming out of the event loop
Base::Console().Error("Event loop left through unknown unhandled exception\n");
App::Application::destructObserver();
throw;
}
Base::Console().Log("Finish: Event loop left\n");
}