WELSIM - Quantify the Uncertain
WELSIM is the #1 engineering simulation software for the open-source community.
Thursday, May 28, 2026
工程仿真软件中的网格2:单元尺寸控制
现代工程仿真软件都可以进行复杂的网格划分,控制网格单元大小是所有复杂网格划分中最常用到的功能。按照指定几何实体来区分,常见的单元尺寸控制分别体现在几何点,几何边,几何面,和几何体四个层级。一个很好的单元尺寸控制功能,可以帮助使用者快速地得到高质量且优化的网格,有助于后续的数值计算。本文从开发者角度,讨论精确控制单元尺寸的应用与技术细节。
Image
特征长度
特征长度是网格划分中的最重要数值。在对几何体(如STEP文件导入生成的BRep实体)进行有限元网格划分时,我们都会设置一个或多个全局特征长度,最常见的特征长度就是最大单元尺寸,这个值限制了单元的大小,从而达到控制网格密度的效果。当然,算法上也可以根据几何体的Bounding Box大小或曲线曲率,内部生成一些特征长度,在每个节点位置比较这些特征长度并取最小值。
实际网格划分中,由于几何体的复杂性,需要人为对某一特定位置加密网格,这就需要对特定位置设定局部特征长度。而局部特征长度优先级会大于全局特征长度,覆盖全局特征长度,参与网格划分。虽然用户常选择的局部位置是点、线、面,而在网格空间,这些选择的区域都转化为笛卡尔坐标。生成网格时,算法会根据当前节点的位置,通过判定和比较,而决定最终的特称长度大小。
示例
以三维立方体模型为例,选择六个面中的一个,并设置所选面的最大单元尺寸为0.2。
Image
网格划分后,可以看到,在所选面及其附近,网格密度明显大于其他位置。
Image
本例只选择了一个面,用户也可以选择多个面,如果是装配体模型,可以选择多个体上的不同的面。同样的,如果选择一条几何边进行加密,也是可以实现。
Image
或者是选择一个几何节点,可以实现对局部节点的网格加密。
Image
总结
有限元网格的局部密度取决于局部单元的特征长度。当同一区域有多个特征长度时,算法上会比较得出最小的特征长度,用于网格划分。对于常见的Delaunay或者Advancing Front算法,特征长度会用于确定下一个相邻网格节点的位置。因为所选的几何实体都会转换为笛卡尔空间坐标,无论前端GUI选择的是几何面,边,还是点,算法层面都没有区别,都是根据每个节点的空间位置来确定特征长度。
Wednesday, May 20, 2026
The mesh of engineering simulation 1: data structures
Modern engineering simulation software is feature-rich. Being the first stage of any simulation pipeline, meshing plays a critical role across the entire software system. The functionality, stability, and efficiency of the meshing module directly impact the usability of the overall software, as meshing is intimately linked to computational functionality, 3D visualization, and human-computer interaction. This blog series will explore the meshing module in engineering simulation software from a developer’s perspective, covering mesh generation, algorithms, data structures, and specialized features. As the first article in the series, this piece discusses mesh data structures and their import/export operations.
Press enter or click to view image in full size
Data structures of finite element mesh
Finite element software connects CAD software to solvers; it takes in geometric topology data from upstream CAD models and generates meshes for downstream solvers. Thus, finite element mesh data structure must bridge the gap between the two by incorporating information from both fields. On top of that, it must support 3D mesh visualization, requiring developers to meticulously develop the underlying data structures. The most common approach is to anchor the design in geometric topology, having each geometric entity contain its lower-level constituents along with the associated finite element node and element information.
Press enter or click to view image in full size
For example, a geometric model, named Model in the diagram above, might contain data blocks named Region, Face, Edge, and Vertex. This is essentially a simplified Boundary Representation (B-Rep) format — a method that represents solid objects using their boundaries (faces, edges, and vertices), allowing exact curved surfaces and precise topological geometry. Ranked from the highest to the lowest hierarchical level, the topology follows:
Compound -> CompSolid -> Solid -> Shell -> Face -> Loop -> Wire -> Edge -> Vertex
Become a Medium member
This is the standard B-Rep data scheme used by most CAD engines. In this hierarchy:
A Wire differs from an Edge in that a Wire possesses curvature.
A Loop is a closed sequence of edges that forms the boundary of a Face.
A Shell is a connected set of Faces.
A CompSolid consists of multiple attached Solids, representing the data format for multi-body parts. Mesh generation requires the special handling of a CompSolid to ensure that the mesh aligns perfectly along embedded faces and embedded edges. (The details of B-Rep data will be discussed in depth in future).
The mapping of individual data components is structured as follows:
Region: Points to CompSolid and Solid geometries. It contains all geometric face information, all embedded geometric entities, and mesh volumetric element information.
Face: Points to Face and Shell geometries. It contains all geometric edge information, the parent geometry it belongs to, embedded geometric entities, and mesh face element information (such as triangular, quadrilateral, and polygonal elements).
Edge: Points to Wire and Edge geometries. It contains all geometric vertex information, the parent geometric faces, and the associated mesh line elements.
Vertex: Points to Vertex geometries. It contains information about the associated geometric vertex, mesh nodes, and the parent geometric edges.
This mapping approach enables rapid queries between mesh and geometry data — such as retrieving all surface elements or nodes on a specific geometric face — which is frequently needed when applying boundary conditions in finite element analysis.
Geometry-to-Mesh Conversion
There are two primary types of conversions from B-Rep to mesh data:
Tessellation/Triangulation: Converts geometry into surface meshes purely for visualization purposes.
Solid Meshing: Generates volumetric meshes used for both finite element computation and mesh visualization (e.g., using VTK’s unstructured grid data format).
Modern finite element software should support both of these conversions, meanwhile reverse-engineering a mesh back into a B-Rep model is rarely implemented because it is nearly impossible to achieve flawlessly.
Import and export of mesh data
Mesh data involves heavy traffic. Beyond supporting their own proprietary mesh formats, modern simulation software must also support universal formats. For finite element, finite volume, and spectral element meshes, widely used formats include UNV, VTU (PVTU), GMSH, EXO, CGNS, MED, and INP, each offering unique advantages.
Press enter or click to view image in full size
Developers can directly leverage these formats or their corresponding SDKs to build core software data structures and handle mesh file I/O:
UNV is a feature-rich and easy-to-use mesh format that supports a wide array of mesh elements. It allows the definition of group information (vital for boundary conditions) and supports both ASCII and Binary formats for read/write operations.
VTU holds a natural advantage in graphical visualization. It’s backed by the entire open-source VTK source code and ecosystem, making it an excellent format for mesh data I/O.
Currently, WELSIM primarily utilizes the UNV and VTU formats as its foundational mesh data structure, fully supporting data import and export for both.
As simulation software scales with additional features, developers often need to incorporate supplementary data. This includes mapping relationships between CAD topology and the mesh, hierarchical relationships for remeshing, and mesh mapping between sub-models and global models. Because these datasets vary in size, they require a flexible, compressible data format — ideally the HDF5 file format. Upon completing a mesh generation, WELSIM produces a file named s2m.h5 to store the mapping relationships between the geometric topology and the mesh.
Conclusion
This article discussed the mesh data structures and I/O methodologies use in simulation software — establishing a proven framework for the meshing modules of general-purpose simulation platforms. It fulfills most functional requirements and adapts exceptionally well to advanced meshing features, like real-time simulation and parallel computing. Furthermore, other complex, specialized features can be successfully developed on top of this foundational data structure, such as boundary layer meshing and internal embedded surface meshing for multi-body parts.
Saturday, May 16, 2026
工程仿真软件中的网格1:数据结构
现代工程仿真软件的功能丰富,网格作为所有仿真计算的入口模块,在整个软件中占据着越来越重要的位置。网格模块的功能、稳定性与效率,直接影响到整个仿真软件的使用。网格直接关系到软件的计算功能,三维显示,人机交互等方面。本系列从开发者的角度探讨工程仿真软件中的网格模块,内容覆盖网格划分,算法,数据结构,特殊功能等。本文作为系列的第一篇文章,讨论网格的数据结构及其输入输出。
Image
有限元网格的数据结构
有限元软件向上承接CAD软件的几何拓扑数据,向下生成求解器支持的网格,因此有限元网格数据需要包含两者的信息,起到承上启下的作用。同时,还需要支持网格的三维显示,因此开发者必须要对数据结构进行精心地构架。最常见的方式,是以几何拓扑为出发点,每个几何体(entity)都包含下一级的几何体和相关的有限元节点与单元信息。
Image
如几何模型命名为Model,会包含名为Region,Face, Edge,和Vertex数据,这本质上是一种简化版的B-Rep格式,即用物体的边界(面/边/点)来表示实体,是精确的曲面与精确的拓扑数据几何。从层级由高到低分类,
Compound → CompSolid → Solid → Shell → Face → Loop → Wire-> Edge → Vertex
这也是常见CAD引擎的B-Rep数据方式,其中Wire与Edge的区别是Wire会有曲率,Loop是闭合边序列,构成Face的边界。Shell是连通的Face集合。CompSolid是多个Solid粘连,是多体零件的数据形式。网格划分对于CompSolid要对做特殊处理,需要确保网格要沿着共享内表面和共享边划分。关于B-Rep数据的细节会在以后的文章中详细讨论。
Region数据指向的是CompSolid和Solid几何体,包含所有的几何面信息,内部的几何面,几何边,几何节点,和网格体单元信息。
Face数据指向的是Face和Shell几何体,包含所有几何边信息,所属的几何体,内部的几何边与几何节点信息,以及网格面单元信息,如三角单元,四边形单元,和多边形单元。
Edge数据指向的是Wire与Edge几何体,包含所有的几何节点信息,所属的几何面,所含有的网格线单元。
Vertex数据指向Vertex几何体,包含所属几何节点与网格节点信息,所属几何边的信息。
这样的数据映射方式,可以快速的查找网格与几何体数据,如获得某一个几何面上的网格面单元或节点,这在有限元计算的边界条件上经常用到。
B-Rep与网格的转换有两种,一种是通过tessellation/triangulation网格化/三角化转变为表面网格,用于显示几何体。另一种是实体网格,用于有限元计算和网格显示,如使用VTK的非结构网格数据。现代有限元软件,两种转换都是必备的。而逆向的网格转换为B-Rep几乎难以完美实现,因此很少应用。
网格数据的输入与输出
网格数据有大量的交互需求,现代仿真软件除了支持自有的网格格式外,还需要能够支持常见的通用格式。对于有限元(包含有限体积,谱单元)等类型的网格,常见的格式有UNV, VTU(PVTU), GMSH,EXO,CGNS, MED, INP等。这些格式都各有优点。
Image
开发者可以直接应用这些格式或开发包,来构建软件的核心数据结构和读写网格文件。UNV是一款功能丰富且易于使用的网格格式,支持大量的网格元素,如定义可以支持边界条件的组信息,读写都有ASCII与Binary两种格式支持。VTU在图形显示上有着天然的优势,有整个VTK开源代码与生态的支持,也是一款很好的网格数据读写格式。目前WELSIM主要使用UNV和VTU两种格式作为基础网格数据,同时支持这些格式的数据导入与到处。
当仿真软件的功能变多时,开发者可能还需要增加其他的数据。如CAD拓扑信息与网格的映射关系,动网格数据每个层级的关系,子模型与全局模型的网格映射等等。这些数据是基础网格数据的重要补充,由于这些数据大小可能不一,需要一种灵活且可压缩的数据格式来支持,比较理想的是用Hdf5格式文件。WELSIM会在网格划分完毕后,生成一个名为s2m.h5的文件,来包含几何拓扑与网格的映射关系。
总结
本文讨论了仿真软件的网格数据结构与输入输出方式。是被验证了可以应用于通用仿真软件的网格模块。可以满足大部分功能需求。甚至对于一些高级网格功能,如实时仿真,并行计算都能够很好的适应。对于一些复杂的特殊功能,如边界层网络,多体零件的内部嵌入面网格划分,都可以在此数据结构上进行开发。
Saturday, April 4, 2026
WELSIM releases with all new dark theme GUI
WELSIM, a general-purpose engineering simulation and analysis software, has released its latest 2026R2 version (internal version 3.3). Compared to the previous release, version 2026R2 introduces a majorly upgraded underlying framework alongside a new dark theme, improving the graphical user interface.
All new dark theme
The newly added dark theme reflects current desktop application designs and is gentler on the eyes. Users can set the appearance theme in Preferences and have three options available: Classic, Dark, and Light.
Press enter or click to view image in full size
The Classic theme is similar to the Light theme, whereas the Dark theme features a dark background with light‑colored text and icons in the foreground. Starting with this version 2026R2, WELSIM’s default style will be the Dark theme.
Comprehensive Framework Upgrade
The new version upgrades the key foundational framework, including updating the GUI framework Qt from 5.15 to 6.10 and the 3D rendering engine VTK from 8.2 to 9.2.
Press enter or click to view image in full size
Upgrading both core frameworks across major versions has improved WELSIM’s stability and performance.
Other Improvements and Upgrades
The new release optimizes and advances existing features and adds more automated test cases, delivering a better user experience in WELSIM.
Become a Medium member
Disclaimer: WelSim and its developers are not affiliated with Qt or VTK, nor are they associated with the above‑mentioned development teams or organizations. References to Qt and VTK are made solely for reference in technical blog posts and software documentation.
Friday, April 3, 2026
WELSIM发布2026R2版本,增加暗色外观模式
通用工程仿真分析软件WELSIM发布了最新的2026R2版本(内部版本号3.3)。相对于上一个版本,2026R2版本对基础框架进行了重大升级,同时新增的暗色外观模式,图形化界面变得更加友好。
Image
增加暗色外观主题
新增的暗色外观主题符合现代桌面软件的趋势,对用户的眼睛与视力更加友好。用户可以在首选项中,设置外观主题,目前有经典,暗色和亮色三个主题可供选择。经典和亮色主题相似,暗色主题以深色为背景,亮色为前端文字和图标。从此版本开始,暗色主题是WELSIM的默认外观。
Image
基础框架的全面升级
新版本升级了主要的基础框架,包括将大型可视化框架Qt 从5.15升级到6.10,三维渲染引擎VTK从8.2升级到9.2版本。这两个核心基础框架的跨主版本升级,令软件更加稳定,性能更佳。
Image
其他增强与升级
此外,新版本还对已有功能进行了优化与升级,增加了更多的自动化测试算例。使得WELSIM更稳定更加易于使用。
WelSim与作者不隶属于Qt和VTK, 和以上开发团队与机构没有关系。这里引用Qt, VTK 仅用作技术博客文章与软件使用的参考。
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开发团队与机构没有直接关系。这里引用这些开源软件的名称和图片仅用作技术博客文章与软件使用的参考。
Subscribe to:
Posts (Atom)