App: Running FreeCAD in verbose mode information to reflect Gui -> Help -> About Dialog info (#20487)

This commit is contained in:
Alex Tran
2025-04-15 23:29:07 -07:00
committed by GitHub
parent 4c09e6c3d9
commit 989a06ea63
7 changed files with 354 additions and 293 deletions

View File

@@ -64,7 +64,9 @@
#include <QDir>
#include <QFileInfo>
#include <QProcessEnvironment>
#include <QSettings>
#include <QStandardPaths>
#include <Inventor/C/basic.h>
#include <LibraryVersions.h>
#include <App/MaterialPy.h>
@@ -172,6 +174,7 @@ FC_LOG_LEVEL_INIT("App", true, true)
using namespace App;
namespace sp = std::placeholders;
namespace fs = std::filesystem;
//==========================================================================
// Application
@@ -2384,28 +2387,10 @@ void parseProgramOptions(int ac, char ** av, const std::string& exe, boost::prog
void processProgramOptions(const boost::program_options::variables_map& vm, std::map<std::string,std::string>& mConfig)
{
if (vm.count("version")) {
if (vm.count("version") && !vm.count("verbose")) {
std::stringstream str;
str << mConfig["ExeName"] << " " << mConfig["ExeVersion"]
<< " Revision: " << mConfig["BuildRevision"] << '\n';
if (vm.count("verbose")) {
str << "\nLibrary versions:\n";
str << "boost " << BOOST_LIB_VERSION << '\n';
str << "Coin3D " << fcCoin3dVersion << '\n';
str << "Eigen3 " << fcEigen3Version << '\n';
#ifdef OCC_VERSION_STRING_EXT
str << "OCC " << OCC_VERSION_STRING_EXT << '\n';
#endif
str << "Qt " << QT_VERSION_STR << '\n';
str << "Python " << PY_VERSION << '\n';
str << "PySide " << fcPysideVersion << '\n';
str << "shiboken " << fcShibokenVersion << '\n';
#ifdef SMESH_VERSION_STR
str << "SMESH " << SMESH_VERSION_STR << '\n';
#endif
str << "VTK " << fcVtkVersion << '\n';
str << "xerces-c " << fcXercescVersion << '\n';
}
throw Base::ProgramInformation(str.str());
}
@@ -2535,6 +2520,7 @@ void processProgramOptions(const boost::program_options::variables_map& vm, std:
}
}
}
}
// clang-format on
@@ -2670,7 +2656,7 @@ void Application::initConfig(int argc, char ** argv)
_pConsoleObserverFile = nullptr;
// Banner ===========================================================
if (mConfig["RunMode"] != "Cmd") {
if (mConfig["RunMode"] != "Cmd" && !(vm.count("verbose") && vm.count("version"))) {
// Remove banner if FreeCAD is invoked via the -c command as regular
// Python interpreter
if (mConfig["Verbose"] != "Strict")
@@ -2781,6 +2767,11 @@ void Application::initConfig(int argc, char ** argv)
logStatus();
if (vm.count("verbose") && vm.count("version")) {
Application::_pcSingleton = new Application(mConfig);
throw Base::ProgramInformation(Application::verboseVersionEmitMessage);
}
}
void Application::SaveEnv(const char* s)
@@ -3549,3 +3540,261 @@ std::string Application::FindHomePath(const char* sCall)
#else
# error "std::string Application::FindHomePath(const char*) not implemented"
#endif
QString Application::prettyProductInfoWrapper()
{
auto productName = QSysInfo::prettyProductName();
#if QT_VERSION < QT_VERSION_CHECK(6, 5, 0)
#ifdef FC_OS_MACOSX
auto macosVersionFile =
QStringLiteral("/System/Library/CoreServices/.SystemVersionPlatform.plist");
auto fi = QFileInfo(macosVersionFile);
if (fi.exists() && fi.isReadable()) {
auto plistFile = QFile(macosVersionFile);
plistFile.open(QIODevice::ReadOnly);
while (!plistFile.atEnd()) {
auto line = plistFile.readLine();
if (line.contains("ProductUserVisibleVersion")) {
auto nextLine = plistFile.readLine();
if (nextLine.contains("<string>")) {
QRegularExpression re(QStringLiteral("\\s*<string>(.*)</string>"));
auto matches = re.match(QString::fromUtf8(nextLine));
if (matches.hasMatch()) {
productName = QStringLiteral("macOS ") + matches.captured(1);
break;
}
}
}
}
}
#endif
#endif
#ifdef FC_OS_WIN64
QSettings regKey {
QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
QSettings::NativeFormat};
if (regKey.contains(QStringLiteral("CurrentBuildNumber"))) {
auto buildNumber = regKey.value(QStringLiteral("CurrentBuildNumber")).toInt();
if (buildNumber > 0) {
if (buildNumber < 9200) {
productName = QStringLiteral("Windows 7 build %1").arg(buildNumber);
}
else if (buildNumber < 10240) {
productName = QStringLiteral("Windows 8 build %1").arg(buildNumber);
}
else if (buildNumber < 22000) {
productName = QStringLiteral("Windows 10 build %1").arg(buildNumber);
}
else {
productName = QStringLiteral("Windows 11 build %1").arg(buildNumber);
}
}
}
#endif
return productName;
}
void Application::addModuleInfo(QTextStream& str, const QString& modPath, bool& firstMod)
{
QFileInfo mod(modPath);
if (mod.isHidden()) { // Ignore hidden directories
return;
}
if (firstMod) {
firstMod = false;
str << "Installed mods: \n";
}
str << " * " << (mod.isDir() ? QDir(modPath).dirName() : mod.fileName());
try {
auto metadataFile =
std::filesystem::path(mod.absoluteFilePath().toStdString()) / "package.xml";
if (std::filesystem::exists(metadataFile)) {
App::Metadata metadata(metadataFile);
if (metadata.version() != App::Meta::Version()) {
str << QLatin1String(" ") + QString::fromStdString(metadata.version().str());
}
}
}
catch (const Base::Exception& e) {
auto what = QString::fromUtf8(e.what()).trimmed().replace(QChar::fromLatin1('\n'),
QChar::fromLatin1(' '));
str << " (Malformed metadata: " << what << ")";
}
QFileInfo disablingFile(mod.absoluteFilePath(), QStringLiteral("ADDON_DISABLED"));
if (disablingFile.exists()) {
str << " (Disabled)";
}
str << "\n";
}
QString Application::getValueOrEmpty(const std::map<std::string, std::string>& map, const std::string& key) {
auto it = map.find(key);
return (it != map.end()) ? QString::fromStdString(it->second) : QString();
}
void Application::getVerboseCommonInfo(QTextStream& str, const std::map<std::string,std::string>& mConfig)
{
std::map<std::string, std::string>::iterator it;
const QString deskEnv =
QProcessEnvironment::systemEnvironment().value(QStringLiteral("XDG_CURRENT_DESKTOP"),
QString());
const QString deskSess =
QProcessEnvironment::systemEnvironment().value(QStringLiteral("DESKTOP_SESSION"),
QString());
const QString major = getValueOrEmpty(mConfig, "BuildVersionMajor");
const QString minor = getValueOrEmpty(mConfig, "BuildVersionMinor");
const QString point = getValueOrEmpty(mConfig, "BuildVersionPoint");
const QString suffix = getValueOrEmpty(mConfig, "BuildVersionSuffix");
const QString build = getValueOrEmpty(mConfig, "BuildRevision");
const QString buildDate = getValueOrEmpty(mConfig, "BuildRevisionDate");
QStringList deskInfoList;
QString deskInfo;
if (!deskEnv.isEmpty()) {
deskInfoList.append(deskEnv);
}
if (!deskSess.isEmpty()) {
deskInfoList.append(deskSess);
}
const QString sysType = QSysInfo::productType();
if (sysType != QLatin1String("windows") && sysType != QLatin1String("macos")) {
QString sessionType = QProcessEnvironment::systemEnvironment().value(QStringLiteral("XDG_SESSION_TYPE"),
QString());
if (sessionType == QLatin1String("x11")) {
sessionType = QStringLiteral("xcb");
}
deskInfoList.append(sessionType);
}
if (!deskInfoList.isEmpty()) {
deskInfo = QLatin1String(" (") + deskInfoList.join(QLatin1String("/")) + QLatin1String(")");
}
str << "OS: " << prettyProductInfoWrapper() << deskInfo << '\n';
if (QSysInfo::buildCpuArchitecture() == QSysInfo::currentCpuArchitecture()) {
str << "Architecture: " << QSysInfo::buildCpuArchitecture() << "\n";
}
else {
str << "Architecture: " << QSysInfo::buildCpuArchitecture()
<< "(running on: " << QSysInfo::currentCpuArchitecture() << ")\n";
}
str << "Version: " << major << "." << minor << "." << point << suffix << "." << build;
#ifdef FC_CONDA
str << " Conda";
#endif
#ifdef FC_FLATPAK
str << " Flatpak";
#endif
const char* appimage = getenv("APPIMAGE");
if (appimage) {
str << " AppImage";
}
const char* snap = getenv("SNAP_REVISION");
if (snap) {
str << " Snap " << snap;
}
str << '\n';
str << "Build date: " << buildDate << "\n";
#if defined(_DEBUG) || defined(DEBUG)
str << "Build type: Debug\n";
#elif defined(NDEBUG)
str << "Build type: Release\n";
#elif defined(CMAKE_BUILD_TYPE)
str << "Build type: " << CMAKE_BUILD_TYPE << '\n';
#else
str << "Build type: Unknown\n";
#endif
const QString buildRevisionBranch = getValueOrEmpty(mConfig, "BuildRevisionBranch");
if (!buildRevisionBranch.isEmpty()) {
str << "Branch: " << buildRevisionBranch << '\n';
}
const QString buildRevisionHash = getValueOrEmpty(mConfig, "BuildRevisionHash");
if (!buildRevisionHash.isEmpty()) {
str << "Hash: " << buildRevisionHash << '\n';
}
// report also the version numbers of the most important libraries in FreeCAD
str << "Python " << PY_VERSION << ", ";
str << "Qt " << QT_VERSION_STR << ", ";
str << "Coin " << COIN_VERSION << ", ";
str << "Vtk " << fcVtkVersion << ", ";
str << "boost " << BOOST_LIB_VERSION << ", ";
str << "Eigen3 " << fcEigen3Version << ", ";
str << "PySide " << fcPysideVersion << '\n';
str << "shiboken " << fcShibokenVersion << ", ";
#ifdef SMESH_VERSION_STR
str << "SMESH " << SMESH_VERSION_STR << ", ";
#endif
str << "xerces-c " << fcXercescVersion << ", ";
const char* cmd = "import ifcopenshell\n"
"version = ifcopenshell.version";
PyObject * ifcopenshellVer = nullptr;
try {
ifcopenshellVer = Base::Interpreter().getValue(cmd, "version");
}
catch (const Base::Exception& e) {
Base::Console().Log("%s (safe to ignore, unless using the BIM workbench and IFC).\n", e.what());
}
if (ifcopenshellVer) {
const char* ifcopenshellVerAsStr = PyUnicode_AsUTF8(ifcopenshellVer);
if (ifcopenshellVerAsStr) {
str << "IfcOpenShell " << ifcopenshellVerAsStr << ", ";
}
Py_DECREF(ifcopenshellVer);
}
#if defined(HAVE_OCC_VERSION)
str << "OCC " << OCC_VERSION_MAJOR << "." << OCC_VERSION_MINOR << "." << OCC_VERSION_MAINTENANCE
#ifdef OCC_VERSION_DEVELOPMENT
<< "." OCC_VERSION_DEVELOPMENT
#endif
<< '\n';
#endif
QLocale loc;
str << "Locale: " << QLocale::languageToString(loc.language()) << "/"
#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
<< QLocale::countryToString(loc.country())
#else
<< QLocale::territoryToString(loc.territory())
#endif
<< " (" << loc.name() << ")";
if (loc != QLocale::system()) {
loc = QLocale::system();
str << " [ OS: " << QLocale::languageToString(loc.language()) << "/"
#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
<< QLocale::countryToString(loc.country())
#else
<< QLocale::territoryToString(loc.territory())
#endif
<< " (" << loc.name() << ") ]";
}
str << "\n";
}
void Application::getVerboseAddOnsInfo(QTextStream& str, const std::map<std::string,std::string>& mConfig) {
// Add installed module information:
const auto modDir = fs::path(Application::getUserAppDataDir()) / "Mod";
bool firstMod = true;
if (fs::exists(modDir) && fs::is_directory(modDir)) {
for (const auto& mod : fs::directory_iterator(modDir)) {
auto dirName = mod.path().string();
addModuleInfo(str, QString::fromStdString(dirName), firstMod);
}
}
const QString additionalModules = getValueOrEmpty(mConfig, "AdditionalModulePaths");
if (!additionalModules.isEmpty()) {
auto mods = additionalModules.split(QChar::fromLatin1(';'));
for (const auto& mod : mods) {
addModuleInfo(str, mod, firstMod);
}
}
}

View File

@@ -25,6 +25,7 @@
#define SRC_APP_APPLICATION_H_
#include <boost/signals2.hpp>
#include <QtCore/qtextstream.h>
#include <deque>
#include <vector>
@@ -433,6 +434,16 @@ public:
static std::string getHelpDir();
//@}
/** @name Verbose Information */
//@{
static void getVerboseCommonInfo(QTextStream& str, const std::map<std::string,std::string>& mConfig);
static void getVerboseAddOnsInfo(QTextStream& str, const std::map<std::string,std::string>& mConfig);
static void addModuleInfo(QTextStream& str, const QString& modPath, bool& firstMod);
static QString prettyProductInfoWrapper();
static QString getValueOrEmpty(const std::map<std::string, std::string>& map, const std::string& key);
static constexpr const char* verboseVersionEmitMessage{"verbose_version"};
//@}
/** @name Link handling */
//@{

View File

@@ -35,6 +35,7 @@
#include <QMessageLogContext>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include <QScreen>
#include <QStatusBar>
#include <QStyle>
#include <QTextStream>
@@ -2631,3 +2632,44 @@ App::Document* Application::reopen(App::Document* doc)
}
return doc;
}
void Application::getVerboseDPIStyleInfo(QTextStream& str) {
// Add Stylesheet/Theme/Qtstyle information
std::string styleSheet =
App::GetApplication()
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/MainWindow")
->GetASCII("StyleSheet");
std::string theme =
App::GetApplication()
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/MainWindow")
->GetASCII("Theme");
#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
std::string style = qApp->style()->name().toStdString();
#else
std::string style =
App::GetApplication()
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/MainWindow")
->GetASCII("QtStyle");
if (style.empty()) {
style = "Qt default";
}
#endif
if (styleSheet.empty()) {
styleSheet = "unset";
}
if (theme.empty()) {
theme = "unset";
}
str << "Stylesheet/Theme/QtStyle: " << QString::fromStdString(styleSheet) << "/"
<< QString::fromStdString(theme) << "/" << QString::fromStdString(style) << "\n";
// Add DPI information
str << "Logical DPI/Physical DPI/Pixel Ratio: "
<< QApplication::primaryScreen()->logicalDotsPerInch()
<< "/"
<< QApplication::primaryScreen()->physicalDotsPerInch()
<< "/"
<< QApplication::primaryScreen()->devicePixelRatio()
<< "\n";
}

View File

@@ -252,6 +252,9 @@ public:
void tryClose( QCloseEvent * e );
//@}
/// get verbose DPI and style info
static void getVerboseDPIStyleInfo(QTextStream& str);
/// whenever GUI is about to start with the main window hidden
static bool hiddenMainWindow();
/// return the status bits

View File

@@ -47,6 +47,7 @@
#include <Base/Console.h>
#include <Base/Interpreter.h>
#include <CXX/WrapPython.h>
#include <Gui/Application.h>
#include <filesystem>
#include <LibraryVersions.h>
@@ -62,59 +63,6 @@ using namespace Gui;
using namespace Gui::Dialog;
namespace fs = std::filesystem;
static QString prettyProductInfoWrapper()
{
auto productName = QSysInfo::prettyProductName();
#if QT_VERSION < QT_VERSION_CHECK(6, 5, 0)
#ifdef FC_OS_MACOSX
auto macosVersionFile =
QStringLiteral("/System/Library/CoreServices/.SystemVersionPlatform.plist");
auto fi = QFileInfo(macosVersionFile);
if (fi.exists() && fi.isReadable()) {
auto plistFile = QFile(macosVersionFile);
plistFile.open(QIODevice::ReadOnly);
while (!plistFile.atEnd()) {
auto line = plistFile.readLine();
if (line.contains("ProductUserVisibleVersion")) {
auto nextLine = plistFile.readLine();
if (nextLine.contains("<string>")) {
QRegularExpression re(QStringLiteral("\\s*<string>(.*)</string>"));
auto matches = re.match(QString::fromUtf8(nextLine));
if (matches.hasMatch()) {
productName = QStringLiteral("macOS ") + matches.captured(1);
break;
}
}
}
}
}
#endif
#endif
#ifdef FC_OS_WIN64
QSettings regKey {
QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
QSettings::NativeFormat};
if (regKey.contains(QStringLiteral("CurrentBuildNumber"))) {
auto buildNumber = regKey.value(QStringLiteral("CurrentBuildNumber")).toInt();
if (buildNumber > 0) {
if (buildNumber < 9200) {
productName = QStringLiteral("Windows 7 build %1").arg(buildNumber);
}
else if (buildNumber < 10240) {
productName = QStringLiteral("Windows 8 build %1").arg(buildNumber);
}
else if (buildNumber < 22000) {
productName = QStringLiteral("Windows 10 build %1").arg(buildNumber);
}
else {
productName = QStringLiteral("Windows 11 build %1").arg(buildNumber);
}
}
}
#endif
return productName;
}
// ------------------------------------------------------------------------------
AboutDialogFactory* AboutDialogFactory::factory = nullptr;
@@ -296,7 +244,7 @@ void AboutDialog::setupLabels()
ui->labelBuildDate->setText(date);
QString os = ui->labelBuildOS->text();
os.replace(QStringLiteral("Unknown"), prettyProductInfoWrapper());
os.replace(QStringLiteral("Unknown"), App::Application::prettyProductInfoWrapper());
ui->labelBuildOS->setText(os);
QString architecture = ui->labelBuildRunArchitecture->text();
@@ -527,230 +475,14 @@ void AboutDialog::linkActivated(const QUrl& link)
licenseView->setSource(link);
}
void AboutDialog::addModuleInfo(QTextStream& str, const QString& modPath, bool& firstMod)
{
QFileInfo mod(modPath);
if (mod.isHidden()) { // Ignore hidden directories
return;
}
if (firstMod) {
firstMod = false;
str << "Installed mods: \n";
}
str << " * " << (mod.isDir() ? QDir(modPath).dirName() : mod.fileName());
try {
auto metadataFile =
std::filesystem::path(mod.absoluteFilePath().toStdString()) / "package.xml";
if (std::filesystem::exists(metadataFile)) {
App::Metadata metadata(metadataFile);
if (metadata.version() != App::Meta::Version()) {
str << QLatin1String(" ") + QString::fromStdString(metadata.version().str());
}
}
}
catch (const Base::Exception& e) {
auto what = QString::fromUtf8(e.what()).trimmed().replace(QChar::fromLatin1('\n'),
QChar::fromLatin1(' '));
str << " (Malformed metadata: " << what << ")";
}
QFileInfo disablingFile(mod.absoluteFilePath(), QStringLiteral("ADDON_DISABLED"));
if (disablingFile.exists()) {
str << " (Disabled)";
}
str << "\n";
}
void AboutDialog::copyToClipboard()
{
QString data;
QTextStream str(&data);
std::map<std::string, std::string>& config = App::Application::Config();
std::map<std::string, std::string>::iterator it;
QString exe = QString::fromStdString(App::Application::getExecutableName());
QString major = QString::fromStdString(config["BuildVersionMajor"]);
QString minor = QString::fromStdString(config["BuildVersionMinor"]);
QString point = QString::fromStdString(config["BuildVersionPoint"]);
QString suffix = QString::fromStdString(config["BuildVersionSuffix"]);
QString build = QString::fromStdString(config["BuildRevision"]);
QString buildDate = QString::fromStdString(config["BuildRevisionDate"]);
QString deskEnv =
QProcessEnvironment::systemEnvironment().value(QStringLiteral("XDG_CURRENT_DESKTOP"),
QString());
QString deskSess =
QProcessEnvironment::systemEnvironment().value(QStringLiteral("DESKTOP_SESSION"),
QString());
QStringList deskInfoList;
QString deskInfo;
if (!deskEnv.isEmpty()) {
deskInfoList.append(deskEnv);
}
if (!deskSess.isEmpty()) {
deskInfoList.append(deskSess);
}
if (qGuiApp->platformName() != QLatin1String("windows")
&& qGuiApp->platformName() != QLatin1String("cocoa")) {
deskInfoList.append(qGuiApp->platformName());
}
if (!deskInfoList.isEmpty()) {
deskInfo = QLatin1String(" (") + deskInfoList.join(QLatin1String("/")) + QLatin1String(")");
}
str << "OS: " << prettyProductInfoWrapper() << deskInfo << '\n';
if (QSysInfo::buildCpuArchitecture() == QSysInfo::currentCpuArchitecture()) {
str << "Architecture: " << QSysInfo::buildCpuArchitecture() << "\n";
}
else {
str << "Architecture: " << QSysInfo::buildCpuArchitecture()
<< "(running on: " << QSysInfo::currentCpuArchitecture() << ")\n";
}
str << "Version: " << major << "." << minor << "." << point << suffix << "." << build;
#ifdef FC_CONDA
str << " Conda";
#endif
#ifdef FC_FLATPAK
str << " Flatpak";
#endif
char* appimage = getenv("APPIMAGE");
if (appimage) {
str << " AppImage";
}
char* snap = getenv("SNAP_REVISION");
if (snap) {
str << " Snap " << snap;
}
str << '\n';
str << "Build date: " << buildDate << "\n";
#if defined(_DEBUG) || defined(DEBUG)
str << "Build type: Debug\n";
#elif defined(NDEBUG)
str << "Build type: Release\n";
#elif defined(CMAKE_BUILD_TYPE)
str << "Build type: " << CMAKE_BUILD_TYPE << '\n';
#else
str << "Build type: Unknown\n";
#endif
it = config.find("BuildRevisionBranch");
if (it != config.end()) {
str << "Branch: " << QString::fromStdString(it->second) << '\n';
}
it = config.find("BuildRevisionHash");
if (it != config.end()) {
str << "Hash: " << QString::fromStdString(it->second) << '\n';
}
// report also the version numbers of the most important libraries in FreeCAD
str << "Python " << PY_VERSION << ", ";
str << "Qt " << QT_VERSION_STR << ", ";
str << "Coin " << COIN_VERSION << ", ";
str << "Vtk " << fcVtkVersion << ", ";
const char* cmd = "import ifcopenshell\n"
"version = ifcopenshell.version";
PyObject * ifcopenshellVer = nullptr;
try {
ifcopenshellVer = Base::Interpreter().getValue(cmd, "version");
}
catch (const Base::Exception& e) {
Base::Console().Log("%s (safe to ignore, unless using the BIM workbench and IFC).\n", e.what());
}
if (ifcopenshellVer) {
const char* ifcopenshellVerAsStr = PyUnicode_AsUTF8(ifcopenshellVer);
if (ifcopenshellVerAsStr) {
str << "IfcOpenShell " << ifcopenshellVerAsStr << ", ";
}
Py_DECREF(ifcopenshellVer);
}
#if defined(HAVE_OCC_VERSION)
str << "OCC " << OCC_VERSION_MAJOR << "." << OCC_VERSION_MINOR << "." << OCC_VERSION_MAINTENANCE
#ifdef OCC_VERSION_DEVELOPMENT
<< "." OCC_VERSION_DEVELOPMENT
#endif
<< '\n';
#endif
QLocale loc;
str << "Locale: " << QLocale::languageToString(loc.language()) << "/"
#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
<< QLocale::countryToString(loc.country())
#else
<< QLocale::territoryToString(loc.territory())
#endif
<< " (" << loc.name() << ")";
if (loc != QLocale::system()) {
loc = QLocale::system();
str << " [ OS: " << QLocale::languageToString(loc.language()) << "/"
#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
<< QLocale::countryToString(loc.country())
#else
<< QLocale::territoryToString(loc.territory())
#endif
<< " (" << loc.name() << ") ]";
}
str << "\n";
// Add Stylesheet/Theme/Qtstyle information
std::string styleSheet =
App::GetApplication()
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/MainWindow")
->GetASCII("StyleSheet");
std::string theme =
App::GetApplication()
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/MainWindow")
->GetASCII("Theme");
#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
std::string style = qApp->style()->name().toStdString();
#else
std::string style =
App::GetApplication()
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/MainWindow")
->GetASCII("QtStyle");
if (style.empty()) {
style = "Qt default";
}
#endif
if (styleSheet.empty()) {
styleSheet = "unset";
}
if (theme.empty()) {
theme = "unset";
}
str << "Stylesheet/Theme/QtStyle: " << QString::fromStdString(styleSheet) << "/"
<< QString::fromStdString(theme) << "/" << QString::fromStdString(style) << "\n";
// Add DPI information
str << "Logical DPI/Physical DPI/Pixel Ratio: "
<< QApplication::primaryScreen()->logicalDotsPerInch()
<< "/"
<< QApplication::primaryScreen()->physicalDotsPerInch()
<< "/"
<< QApplication::primaryScreen()->devicePixelRatio()
<< "\n";
// Add installed module information:
auto modDir = fs::path(App::Application::getUserAppDataDir()) / "Mod";
bool firstMod = true;
if (fs::exists(modDir) && fs::is_directory(modDir)) {
for (const auto& mod : fs::directory_iterator(modDir)) {
auto dirName = mod.path().string();
addModuleInfo(str, QString::fromStdString(dirName), firstMod);
}
}
auto additionalModules = config.find("AdditionalModulePaths");
if (additionalModules != config.end()) {
auto mods = QString::fromStdString(additionalModules->second).split(QChar::fromLatin1(';'));
for (const auto& mod : mods) {
addModuleInfo(str, mod, firstMod);
}
}
App::Application::getVerboseCommonInfo(str, config);
Gui::Application::getVerboseDPIStyleInfo(str);
App::Application::getVerboseAddOnsInfo(str, config);
QClipboard* cb = QApplication::clipboard();
cb->setText(data);

View File

@@ -38,6 +38,8 @@
#include <cstdio>
#include <sstream>
#include <QString>
// FreeCAD Base header
#include <Base/Console.h>
#include <Base/Exception.h>
@@ -91,6 +93,17 @@ int main(int argc, char** argv)
exit(1);
}
catch (const Base::ProgramInformation& e) {
if (std::strcmp(e.what(), App::Application::verboseVersionEmitMessage) == 0) {
QString data;
QTextStream str(&data);
const std::map<std::string, std::string> config = App::Application::Config();
App::Application::getVerboseCommonInfo(str, config);
App::Application::getVerboseAddOnsInfo(str, config);
std::cout << data.toStdString();
exit(0);
}
std::cout << e.what();
exit(0);
}

View File

@@ -250,6 +250,17 @@ int main(int argc, char** argv)
catch (const Base::ProgramInformation& e) {
QApplication app(argc, argv);
QString msg = QString::fromUtf8(e.what());
if (msg == QLatin1String(App::Application::verboseVersionEmitMessage)) {
QString data;
QTextStream str(&data);
const std::map<std::string, std::string> config = App::Application::Config();
App::Application::getVerboseCommonInfo(str, config);
Gui::Application::getVerboseDPIStyleInfo(str);
App::Application::getVerboseAddOnsInfo(str, config);
msg = data;
}
DisplayInfo(msg);
exit(0);
}