Saturday, March 21, 2026

Multi-style interface for Qt-based desktop software

Qt is one of the most popular cross-platform C++ Graphical User Interface (GUI) application frameworks in the world. Its most powerful feature is its cross-platform capability: developers write code once and, after recompilation, are able to run it on desktop (Windows, macOS, Linux), mobile (iOS, Android), and embedded systems (QNX, VxWorks, various RTOS). Software products, particularly large-scale industrial software, are frequently built on the Qt framework. From a developer perspective, this article discusses how to optimally implement multi-theme interfaces in Qt software, focusing on Dark theme. Press enter or click to view image in full size In regards to the development of large-scale Qt desktop applications, such as IDEs, industrial control software, or productivity tools, the UI is more than just aesthetics; it concerns operational comfort during long usage sessions, the clarity of information hierarchy, and brand professionalism. As a result, modern large-scale visualization software support multiple interface styles, commonly categorized into Light and Dark themes. Because Light mode is typically the default, Dark mode requires dedicated functional implementation by developers. From a practical standpoint, just these two styles are generally sufficient for most needs. Implementation methods There are several established solutions for implementing and dynamically switching between themes in Qt. This article focuses on the two most common methods: Source Code and QSS files. Press enter or click to view image in full size Setting QStyle in C++/Python Source Code This is the classic approach. Developers define different themes directly in the source code, with customized colors for each theme, and apply the themes to the QApplication via the setStyle and setPalette functions. The advantage of this method is its simplicity, speed, and ease of maintenance as developers manage styles alongside other Qt code. However, because user-defined styles require access to the source code, this technique has limited scalability. For large commercial software, especially engineering software, users rarely customize interface styles anyways, making direct QStyle configuration in source code an efficient solution. The general-purpose engineering simulation software WELSIM also adopts this approach. Setting QStyleSheet via QSS This is a more flexible and adaptable method, implemented by loading different .qss files combined with variable substitution (using regular expressions or setProperty). One advantage is the seperation of the style from the application functionality as style modifications do not require recompiling the source code, and end-users can modify or add QSS files to achieve custom styles. The drawbacks include slightly reduced rendering performance for complex interfaces and a relatively cumbersome development and maintenance process that requires a proficiency in QSS syntax. Light Theme vs. Dark Theme Light and dark are the two primary color schemes for multi-style development. The light style is similar to the operating system’s native display, so the main task in developing a multi-theme interface is implementing the dark theme. These two themes can be switched on demand, much like the map system in a car’s central display where it’s a light background during the day and dark background at night. Dark-themed desktop software is becoming increasingly popular because it is eye-friendly, For users of large-scale engineering software who spend extended periods viewing screens, dark interfaces effectively reduce eye strain. Press enter or click to view image in full size Many developers mistakenly believe dark mode is a simple color inversion. In reality, directly flipping white background/black text to black background/white text causes visual fatigue due to extreme contrast and a halo effect. Below are the recommended color schemes for light and dark themes: Dark Theme Background: Avoid pure black (#000000); use dark gray (e.g., #121212 or #1E1E1E) for depth through shadows. Layout: Follow the “lighter is closer” principle. The base background is darkest, while pop-ups or floating dialogs should be slightly lighter. Text: Avoid pure white (#FFFFFF); use off-white or light gray (e.g., #E0E0E0) to reduce glare. Light Theme Background: Choose a very light gray (e.g., #F5F5F5) as the primary background, reserving pure white for editing or core content areas. Contrast: Use subtle borders rather than large shadows to distinguish modules. Color Balance: Ensure accent colors (status bars, icons) have enough saturation to remain visible under bright light. Write on Medium Notably, Qt’s built-in Fusion style is an excellent base theme with built-in light and dark variants; developers can directly use Fusion as a development template. Icons Background color changes may render existing icons incompatible (e.g., icons designed for light themes become lose contrast on dark backgrounds). Qt provides solutions for multi-style icons, with three common methods: 1. Using QStyle Standard Icons Automatically adapts to light/dark themes in Fusion mode — the most convenient option. However, Qt’s built-in standard icons are limited and only sufficient for small software; large applications require custom icons. 2. Dynamic SVG Color Changing Replace fixed colors (e.g., fill=”#000000") in SVG icons with fill=”currentColor”. Qt automatically replaces currentColor with the widget’s foreground color (palette.color(QPalette::WindowText)). This only works for single-color icons; multi-color icons cannot achieve ideal results. 3. Dual Icon Sets (Light + Dark) The standard for large software. Despite needing to create and maintain two icon sets, it is the most efficient method, supporting complex and high-quality icons. This is the approach used for icons in WELSIM's light and dark styles. When using two sets of icon themes, determine whether the application’s current theme is dark. If it is, apply the dark icons; otherwise, use the light icons. There are several ways to detect a dark theme; for Qt 6.5 and above, you can use the following method: bool isDark() { return qApp->styleHints()->colorScheme() == Qt::ColorScheme::Dark; } Dynamically load icons in updateIcon() function. void updateIcon() { QString path = isDark() ? “:/icons/icon_dark.svg” : “:/icons/icon_light.svg”; ui->button->setIcon(QIcon(path)); } Place updateIcon() in the changeEvent(QEvent *e) slot function. Capture system theme changes via e->type() == QEvent::PaletteChange or e->type() == QEvent::StyleChange. OpenGL Rendering Widgets Many large applications include OpenGL-based rendering widgets where Qt’s default QWidget theme settings do not automatically apply. Manual synchronization is required: Set the renderer’s background color to match the QWidget background. Configure canvas background and drawing colors. Press enter or click to view image in full size Taking a VTK-implemented chart as an example, synchronizing the 2D plotting area requires setting the renderer background, chart background, and axis colors, then updating the renderer. The source code for theme synchronization is shown below. void myWelSimChartPlot::syncVtkThemeWithQtDark(QWidget* qtWidget) { vtkRenderWindow* renderWindow = this->renderWindow(); vtkRendererCollection* renderers = renderWindow ? renderWindow->GetRenderers() : nullptr; vtkRenderer* renderer = renderers ? renderers->GetFirstRenderer() : nullptr; if (!qtWidget || !renderer || !renderWindow) return; const QPalette palette = qtWidget->palette(); const QColor bgColor = palette.color(QPalette::Base); const QColor fgColor = palette.color(QPalette::WindowText); renderer->SetBackground(qColorToVtk(bgColor)); vtkColor4ub color; color.Set(bgColor.red(), bgColor.green(), bgColor.blue(), bgColor.alpha()); this->chart()->GetBackgroundBrush()->SetColor(color); int axisIDs[4] = { vtkAxis::BOTTOM, vtkAxis::TOP, vtkAxis::LEFT, vtkAxis::RIGHT }; for (unsigned int axisIndex = 0; axisIndex < 4; ++axisIndex) { int axisID = axisIDs[axisIndex]; vtkAxis* axis = this->chart()->GetAxis(axisID); if (!axis) { assert(0); continue; } axis->GetTitleProperties()->SetColor(qColorToVtk(fgColor)); axis->GetLabelProperties()->SetColor(qColorToVtk(fgColor)); axis->GetGridPen()->SetColorF(qColorToVtk(borderColor)); axis->GetPen()->SetColorF(qColorToVtk(fgColor)); } renderWindow->Render(); } In addition to the 2D drawing window, OpenGL-driven 3D windows can also be configured with corresponding color themes. However, in actual product design, this is often unnecessary because 3D windows provide their own color schemes. As shown in the figure, WELSIM’s 3D rendering window supports three background options: blue-white gradient, black, and white. Users can customize the style and color of the 3D OpenGL rendering window to fit their needs. Press enter or click to view image in full size Press enter or click to view image in full size Conclusion Nowadays, for any well-established large-scale engineering software, supporting multiple themes has become an essential feature. At the very least, the software should include both Light and Dark modes. The core challenge lies in the implementation of the Dark theme because although the technical difficulty is not necessarily high, it involves a vast amount of meticulous development and testing. This takes significant development time as well as long-term optimization and maintenance. If the existing codebase contains local style modifications, such as setStyle or setStyleSheet functions, they must be updated accordingly. From a maintenance perspective, it is best to remove those local styling snippets and instead allow a global style manager to govern the entire software interface. WELSIM and its author are not affiliated with Qt or the Qt development team/organization. All open-source software names and images referenced herein are for technical blog and software usage reference only.

Friday, March 20, 2026

Qt桌面软件的多主题风格界面

Qt 是全球最流行的跨平台 C++ 图形用户界面(GUI)应用程序开发框架之一。Qt 最强大的特性在于其跨平台能力。开发者编写一次代码,经过重新编译,即可在桌面端(Windows, macOS, Linux),移动端(iOS, Android),和嵌入式(QNX, VxWorks, 各种 RTOS)系统中运行。已经有大量的软件产品,尤其是大型工业软件,都是基于Qt的框架开发而成的。本文从实际开发的角度,讨论如何最优地实现Qt软件的多主题风格界面,尤其是暗色风格。 在开发大型 Qt 桌面程序(如 IDE、工业控制软件或生产力工具)时,界面(UI)不仅仅是“好看”,更关乎长时间操作的舒适度、信息层级的清晰度以及品牌专业感。因此现代大型可视化软件,都实现了多种界面风格,从色彩来区分,常见的有暗色和亮色主题两种风格。亮色通常是默认风格,而暗色风格需要开发人员专门增加相关的功能。虽然一个软件可以拥有无数个界面风格,但是从实用性角度,只需要两个风格:亮色和暗色。 实现方案 在 Qt 中实现多个主题风格,同时这些风格之间还可以动态切换,已经有一些成熟的方案。本文只讨论最常用的两种实现方式:源码和qss文件。 这两种主流方法细节如下: 1. 在C++或Python源代码中设置QStyle 这是最经典的做法。开发者直接在源码中定义不同主题,每个主题定义好颜色。然后通过setStyle和setPalette函数对QApplication进行设置主题即可。这种方法的好处是开发简单迅速,易于维护,开发者只需要和其他Qt代码一起管理风格。但是拓展性较弱,当用户想要自定义某种风格时,必须要有源代码才能实现。对于大型商业软件,尤其是工程类软件,用户很少会自定义界面风格,因此直接在源代码中设置QStyle是一个比较简单高效的方案。大型通用工程仿真软件WELSIM也选择了这种方式。 2. 通过QSS设置QStyleSheet 这是较为灵活且可扩展的做法。通过加载不同的 .qss 文件,配合变量替换(利用正则或 setProperty)实现。优点是与逻辑解耦,样式修改无需重新编译源码,最终用户也可以修改或添加qss文件来实现想要的风格。缺点是渲染性能在极复杂界面下略有损耗;维护与开发过程相对繁琐,需要熟悉qss文件的语法。 亮色vs暗色 亮色和暗色是开发多风格主题最主要的两个色系。浅色风格和操作系统的原生风格比较相似。因此,在开发多风格界面时,主要工作就是是增加暗色主题。这两种主题可以按照用户的需求随时切换,就好比车载大屏幕的地图系统,白天会显示亮色背景,而夜晚会以暗色背景显示。此外,深色界面的桌面软件越来越多,主要是由于对于软件使用者的眼睛和视力保护较为友好,尤其是大型工程软件,用户会长时间的观看电脑屏幕,暗色界面可以有效缓解用户眼睛的疲劳。 很多开发者误以为暗色模式就是简单的“反相”,其实不然。直接将白底黑字翻转为黑底白字会导致极高的对比度,产生“光晕效应”,导致视觉疲劳。这里给出合适的明暗两种风格的配色方案。 暗色主题 (Dark Theme) 背景: 避免使用纯黑色(#000000),建议使用深灰色(如 #121212 或 #1E1E1E)。深灰色能通过阴影展现深度感。 层级: 遵循“越靠前越亮”的原则。底层背景最暗,弹出窗口或浮动面板应稍微亮一点。 文字: 避免纯白色文字(#FFFFFF),建议使用米白或浅灰(如 #E0E0E0),降低刺眼感。 亮色主题 (Light Theme) 背景: 通常选择极浅的灰色(如 #F5F5F5)而非纯白,作为主背景色,将纯白留给编辑区或核心内容区。 对比: 强调通过细腻的边框(Border)而非大面积阴影来区分模块。 色彩平衡: 辅助色(如状态条、图标)应具备足够的饱和度,以免在强光下显得苍白。 值得一提的是,Qt自带的Fusion风格就是很好的主题,内含亮色和暗色两种主题,开发者可以直接以Fusion风格为模板进行开发。 图标 背景色改变后,之前的图标可能不太适用。如在浅色主题背景下设计的图标,在深色背景下就显得非常不明显。因此就需要对图标管理有一个很好的方式。Qt为多风格主题提供了图标的解决方案。常用的方式有三种。 1. 直接使用 QStyle 提供的标准图标,Fusion 风格下会自动适配明暗主题,这是最方便的方式,但是Qt自带的标准图标数量有限,可以满足小型软件的图标需求。对于大型软件,往往开发者还需要增加新的图标。 2. 使用SVG图片的动态变色功能,核心:将 SVG 图标内的固定颜色(如 fill="#000000")改为 fill="currentColor"。Qt 会自动将 currentColor 替换为控件的前景色(palette.color(QPalette::WindowText))。此方案只适用于单调颜色图标,当图标含有2种颜色以上时,难以实现理想颜色。 3. 使用双图标集,一套浅色,另一套深色。这是大型软件常用的方式,虽然需要建立并维护两套图标,但是方式简单,易于维护。且可以实现复杂且好看的图标集。也是WELSIM的亮暗两种风格的图标集方案。 在使用两套图标集时,判断应用当前的主题是否为深色,如果是深色就应用深色图标,反之则使用浅色图标。判断是否为深色主题的方法有多种,对于Qt6.5以上可以用如下方式: bool isDark() { return qApp->styleHints()->colorScheme() == Qt::ColorScheme::Dark; } 动态加载图标的方式如下: void updateIcon() { QString path = isDark() ? ":/icons/icon_dark.svg" : ":/icons/icon_light.svg"; ui->button->setIcon(QIcon(path)); } 放置上述updateIcon()函数的位置常常是在信号槽接受函数changeEvent(QEvent *e)中。通过e->type() == QEvent::PaletteChange或e->type() == QEvent::StyleChange来捕捉系统的色彩主题变化。 OpenGL渲染窗口 在很多大型软件中,含有带OpenGL绘图的渲染窗口,此时Qt的默认对QWidget的配色主题,无法自动在OpenGL窗口展现。需要对渲染器进行手动的修改,同步色彩主题。需要对Renderer渲染器的背景色设置,与QWidget的背景色一致。同时对画布的背景和绘画颜色进行设置。 下图所示,前后两个表格在不同颜色下的表现。 以一个VTK实现的图表为例。在同步2维绘图区域时,需要设置Renderer的背景,表格的背景,轴线相关的颜色。最后需要更新渲染器来显示新的主题风格。 给出同步颜色主题的代码如下: void myWelSimChartPlot::syncVtkThemeWithQtDark(QWidget* qtWidget) { vtkRenderWindow* renderWindow = this->renderWindow(); vtkRendererCollection* renderers = renderWindow ? renderWindow->GetRenderers() : nullptr; vtkRenderer* renderer = renderers ? renderers->GetFirstRenderer() : nullptr; if (!qtWidget || !renderer || !renderWindow) return; const QPalette palette = qtWidget->palette(); const QColor bgColor = palette.color(QPalette::Base); const QColor fgColor = palette.color(QPalette::WindowText); renderer->SetBackground(qColorToVtk(bgColor)); vtkColor4ub color; color.Set(bgColor.red(), bgColor.green(), bgColor.blue(), bgColor.alpha()); this->chart()->GetBackgroundBrush()->SetColor(color); int axisIDs[4] = { vtkAxis::BOTTOM, vtkAxis::TOP, vtkAxis::LEFT, vtkAxis::RIGHT }; for (unsigned int axisIndex = 0; axisIndex < 4; ++axisIndex) { int axisID = axisIDs[axisIndex]; vtkAxis* axis = this->chart()->GetAxis(axisID); if (!axis) { assert(0); continue; } axis->GetTitleProperties()->SetColor(qColorToVtk(fgColor)); axis->GetLabelProperties()->SetColor(qColorToVtk(fgColor)); axis->GetGridPen()->SetColorF(qColorToVtk(borderColor)); axis->GetPen()->SetColorF(qColorToVtk(fgColor)); } renderWindow->Render(); } 除了二维绘图窗口,OpenGL驱动的三维窗口也可以做相应色彩主题设置。但实际的产品设计中,往往不需要,因为三维窗口会提供其他配色。如图所示,WELSIM的3D渲染窗口支持蓝白渐变,黑色,白色三种背景。用户可以根据需要自行设置3D OpenGL渲染窗口的风格颜色。 总结 现在,一款成熟的大型工程软件,支持多风格主题已经成为一个必备功能。至少具有亮色与暗色两个风格主题。而核心就是实现暗色主题。虽然技术难度不大,但是有大量且琐碎的开发与测试工作,需要一定的开发时间,以及长期的优化与维护。如果已有的代码中有一些局部的修改样式代码,如setStyle, setStyleSheet函数,还需要做相应修改,从易于维护的角度,最好是删除那些局部风格代码。统一由全局风格来管理整个软件的界面。 WelSim与作者不隶属于Qt, 和Qt开发团队与机构没有直接关系。这里引用这些开源软件的名称和图片仅用作技术博客文章与软件使用的参考。