ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvApplicationBase.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include <QDir>
9 #include <QFile>
10 #include <QSettings>
11 #include <QStandardPaths>
12 #include <QString>
13 #include <QStyle>
14 #include <QStyleFactory>
15 #include <QSurfaceFormat>
16 #include <QTextStream>
17 #include <QTranslator>
18 #include <QtGlobal>
19 
20 // CV_CORE_LIB
21 #include <CVPlatform.h>
22 
23 // CV_DB_LIB
24 #include <ecvDisplayTools.h>
25 
26 // Common
27 #include "ecvApplicationBase.h"
28 #include "ecvPersistentSettings.h"
29 #include "ecvPluginManager.h"
30 #include "ecvSettingManager.h"
31 #include "ecvTranslationManager.h"
32 
33 // CV_CORE_LIB
34 #include <CVLog.h>
35 
36 #if (QT_VERSION < QT_VERSION_CHECK(5, 5, 0))
37 #error ACloudViewer does not support versions of Qt prior to 5.5
38 #endif
39 
41  // See
42  // http://doc.qt.io/qt-5/qopenglwidget.html#opengl-function-calls-headers-and-qopenglfunctions
49  {
50  QSurfaceFormat format = QSurfaceFormat::defaultFormat();
51 
52  format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
53  format.setStencilBufferSize(0);
54 
55 #ifdef CV_GL_WINDOW_USE_QWINDOW
56  format.setStereo(true);
57 #endif
58 
59 #ifdef Q_OS_MAC
60  format.setVersion(3, 3); // must be 3.3
61  format.setProfile(QSurfaceFormat::CoreProfile);
62 #endif
63 
64 // #ifdef Q_OS_UNIX
65 // ////enables automatic scaling based on the monitor's pixel density
66 // QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
67 // #endif
68 #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) && \
69  (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
70  // These attributes are deprecated in Qt6 (high DPI is enabled by
71  // default)
72  QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
73  QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
74 #endif
75 
76 #ifdef QT_DEBUG
77  format.setOption(QSurfaceFormat::DebugContext, true);
78 #endif
79  QSurfaceFormat::setDefaultFormat(format);
80  }
81 
82  // The 'AA_ShareOpenGLContexts' attribute must be defined BEFORE the
83  // creation of the Q(Gui)Application DGM: this is mandatory to enable
84  // exclusive full screen for ccGLWidget (at least on Windows)
85  QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
86 }
87 
89  char **argv,
90  bool isCommandLine,
91  const QString &version)
92  : QApplication(argc, argv),
93  c_VersionStr(version),
94  c_CommandLine(isCommandLine) {
95  setOrganizationName("ECVCorp");
96 
97  setupPaths();
98 
99 #ifdef Q_OS_MAC
100  // Mac OS X apps don't show icons in menus
101  setAttribute(Qt::AA_DontShowIconsInMenus);
102 #endif
103 
104  // Force 'english' locale so as to get a consistent behavior everywhere
105  QLocale::setDefault(QLocale::English);
106 
107 #ifdef Q_OS_UNIX
108  // We reset the numeric locale for POSIX functions
109  // See https://doc.qt.io/qt-5/qcoreapplication.html#locale-settings
110  setlocale(LC_NUMERIC, "C");
111 #endif
112 
113  // Restore the style from persistent settings
114  // (matching CloudCompare's approach - using QSettings directly)
115  // Note: We use Qt API directly here instead of setAppStyle() because
116  // CVLog and ecvSettingManager are not yet initialized in the constructor
117  QSettings settings;
118  settings.beginGroup(ecvPS::AppStyle());
119  {
120  QString styleKey = settings.value("style", QString()).toString();
121 
122  // Apply platform-appropriate default if no saved style
123  if (styleKey.isEmpty()) {
124 #ifdef Q_OS_MAC
125  // macOS: Use Fusion for consistent button borders (ParaView
126  // approach)
127  styleKey = "Fusion";
128 #endif
129  }
130 
131  // Apply the style using Qt API directly (safe in constructor)
132  if (!styleKey.isEmpty()) {
133  if (styleKey == "QDarkStyleSheet::Dark") {
134  QFile f(":/qdarkstyle/dark/darkstyle.qss");
135  if (f.open(QFile::ReadOnly | QFile::Text)) {
136  QTextStream ts(&f);
137  setStyleSheet(ts.readAll());
138  f.close();
139  }
140  } else if (styleKey == "QDarkStyleSheet::Light") {
141  QFile f(":/qdarkstyle/light/lightstyle.qss");
142  if (f.open(QFile::ReadOnly | QFile::Text)) {
143  QTextStream ts(&f);
144  setStyleSheet(ts.readAll());
145  f.close();
146  }
147  } else {
148  // Qt native style
149  QStyle *style = QStyleFactory::create(styleKey);
150  if (style) {
151  setStyle(style);
152  }
153  }
154  }
155  }
156  settings.endGroup();
157 
158  ccPluginManager::get().setPaths(m_PluginPaths);
159 
160  ccTranslationManager::get().registerTranslatorFile(QStringLiteral("qt"),
161  m_TranslationPath);
163  QStringLiteral("ACloudViewer"), m_TranslationPath);
165 
166  connect(this, &ecvApplicationBase::aboutToQuit,
167  [=]() { ccMaterial::ReleaseTextures(); });
168 }
169 
170 QString ecvApplicationBase::versionStr() const { return c_VersionStr; }
171 
172 QString ecvApplicationBase::versionLongStr(bool includeOS) const {
173  QString verStr = c_VersionStr;
174 
175 #if defined(CV_ENV_64)
176  const QString arch("64-bit");
177 #elif defined(CV_ENV_32)
178  const QString arch("32-bit");
179 #else
180  const QString arch("\?\?-bit");
181 #endif
182 
183  if (includeOS) {
184 #if defined(CV_WINDOWS)
185  const QString platform("Windows");
186 #elif defined(CV_MAC_OS)
187  const QString platform("macOS");
188 #elif defined(CV_LINUX)
189  const QString platform("Linux");
190 #else
191  const QString platform("Unknown OS");
192 #endif
193  verStr += QStringLiteral(" [%1 %2]").arg(platform, arch);
194  } else {
195  verStr += QStringLiteral(" [%1]").arg(arch);
196  }
197 
198 #ifdef QT_DEBUG
199  verStr += QStringLiteral(" [DEBUG]");
200 #endif
201 
202  return verStr;
203 }
204 
205 const QString &ecvApplicationBase::translationPath() const {
206  return m_TranslationPath;
207 }
208 
209 void ecvApplicationBase::setupPaths() {
210  QDir appDir = QCoreApplication::applicationDirPath();
211 
212  // Set up our shader and plugin paths
213 #if defined(Q_OS_MAC)
214  QDir bundleDir = appDir;
215 
216  if (bundleDir.dirName() == "MacOS") {
217  bundleDir.cdUp();
218  }
219  m_PluginPaths << (bundleDir.absolutePath() + "/cvPlugins");
220  m_PluginPaths << (bundleDir.absolutePath() + "/PlugIns/cvPlugins");
221 
222 #if defined(CV_MAC_DEV_PATHS)
223  // Used for development only - this is the path where the plugins are built
224  // and the shaders are located.
225  // This avoids having to install into the application bundle when
226  // developing.
227  bundleDir.cdUp();
228  bundleDir.cdUp();
229  bundleDir.cdUp();
230 
231  m_PluginPaths << (bundleDir.absolutePath() + "/cvPlugins");
232  m_ShaderPath = (bundleDir.absolutePath() + "/shaders");
233  m_TranslationPath = (bundleDir.absolutePath() + "/app/translations");
234 #else
235  m_ShaderPath = (bundleDir.absolutePath() + "/Shaders");
236  m_TranslationPath = (bundleDir.absolutePath() + "/translations");
237 #endif
238 #elif defined(Q_OS_WIN)
239  m_PluginPaths << (appDir.absolutePath() + "/plugins");
240  m_ShaderPath = (appDir.absolutePath() + "/shaders");
241  m_TranslationPath = (appDir.absolutePath() + "/translations");
242 #elif defined(Q_OS_LINUX) // Q_OS_LINUX
243  // Shaders & plugins are relative to the bin directory where the executable
244  // is found
245  QDir theDir = appDir;
246 
247  if (theDir.dirName() == "bin") {
248  theDir.cdUp();
249  m_PluginPaths << (theDir.absolutePath() + "/plugins");
250  m_PluginPaths << (theDir.absolutePath() + "/bin/plugins");
251  m_PluginPaths << (theDir.absolutePath() + "/lib/ACloudViewer/plugins");
252  m_ShaderPath = (theDir.absolutePath() + "/share/ACloudViewer/shaders");
253  m_TranslationPath =
254  (theDir.absolutePath() + "/share/ACloudViewer/translations");
255  } else {
256  // Choose a reasonable default to look in
257  m_PluginPaths << "/usr/lib/ACloudViewer/plugins";
258  m_PluginPaths << (theDir.absolutePath() + "/plugins");
259  m_PluginPaths << (theDir.absolutePath() + "/bin/plugins");
260  m_PluginPaths << (theDir.absolutePath() + "/lib/ACloudViewer/plugins");
261  m_ShaderPath = "/usr/share/ACloudViewer/shaders";
262  m_TranslationPath = "/usr/share/ACloudViewer/translations";
263  }
264 
265  // check current application translations path whether exists or not
266  // if exist and then overwrite above translation settings.
267  // Priority: bin/translations > build_root/translations > standard paths
268 
269  // First check bin/translations/ (for development builds)
270  QString binTransPath = (appDir.absolutePath() + "/translations");
271  if (QDir(binTransPath).exists()) {
272  m_TranslationPath = binTransPath;
273  } else {
274  // Then check build_root/translations/
275  QString translationPath = (theDir.absolutePath() + "/translations");
276  if (QDir(translationPath).exists()) {
277  m_TranslationPath = translationPath;
278  }
279  }
280 
281 #else
282 #warning Need to specify the shader path for this OS.
283 #endif
284 
285  // Add any app data paths to plugin paths
286  // Plugins in these directories take precendence over the included ones
287  // This allows users to put plugins outside of the install directories.
288  const QStringList appDataPaths =
289  QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
290 
291  for (const QString &appDataPath : appDataPaths) {
292  QString path = appDataPath + "/plugins";
293 
294  if (!m_PluginPaths.contains(path)) // avoid duplicate entries (can
295  // happen, at least on Windows)
296  {
297  m_PluginPaths << path;
298  }
299  }
300 }
301 
302 bool ecvApplicationBase::setAppStyle(const QString &styleKey) {
303  // Helper lambda to load stylesheet from resources
304  const auto loadStyleSheet = [this](const QString &resourcePath) -> bool {
305  QFile f(resourcePath);
306  if (!f.exists()) {
307  return false;
308  }
309 
310  if (!f.open(QFile::ReadOnly | QFile::Text)) {
311  return false;
312  }
313 
314  QTextStream ts(&f);
315  setStyleSheet(ts.readAll());
316  f.close();
317  return true;
318  };
319 
320  // Handle custom stylesheets
321  if (styleKey == "QDarkStyleSheet::Dark") {
322  // Load dark stylesheet from resources
323  if (!loadStyleSheet(":/qdarkstyle/dark/darkstyle.qss")) {
324  return false;
325  }
326  } else if (styleKey == "QDarkStyleSheet::Light") {
327  // Load light stylesheet from resources
328  if (!loadStyleSheet(":/qdarkstyle/light/lightstyle.qss")) {
329  return false;
330  }
331  } else {
332  // Use Qt native styles (Fusion, Windows, macOS, etc.)
333  QStyle *style = QStyleFactory::create(styleKey);
334  if (!style) {
335  CVLog::Warning(QStringLiteral("Invalid style key or style couldn't "
336  "be created: %1")
337  .arg(styleKey));
338  return false;
339  }
340 
341  // Clear any existing stylesheet
342  setStyleSheet({});
343  CVLog::Print(
344  QStringLiteral("Applying application style: %1").arg(styleKey));
345  setStyle(style);
346  }
347 
348  // Save to persistent settings (must be after successful style application)
349  ecvSettingManager::setValue(ecvPS::AppStyle(), "style", styleKey);
350 
351  return true;
352 }
filament::Texture::InternalFormat format
std::string version
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
static void ReleaseTextures()
Release all texture objects.
static ccPluginManager & get()
void setPaths(const QStringList &paths)
static ccTranslationManager & get()
void registerTranslatorFile(const QString &prefix, const QString &path)
QString versionStr() const
QString versionLongStr(bool includeOS) const
const QString & translationPath() const
ecvApplicationBase(int &argc, char **argv, bool isCommandLine, const QString &version)
bool setAppStyle(const QString &styleKey)
Set the application style.
static const QString AppStyle()
static void setValue(const QString &section, const QString &key, const QVariant &value)
static const std::string path
Definition: PointCloud.cpp:59