1. 项目概述:基于Qt与VS的混合开发模式
这个项目采用了一种典型的C++桌面应用开发架构:使用Qt框架构建用户界面,在Visual Studio环境中实现核心业务逻辑。这种组合充分发挥了Qt在UI开发上的优势(跨平台、丰富的控件库、样式定制能力)和VS在代码管理、调试方面的强大功能。
从标题中"程序结构清晰"的描述可以看出,项目采用了良好的架构设计,将界面与逻辑分离。而"画面采用qss"则表明界面样式使用了Qt Style Sheets技术,这是一种类似CSS的声明式样式语言,能够实现高度定制化的视觉效果。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 开发环境搭建与配置
2.1 Qt与VS的版本选择
对于这种开发模式,我推荐使用以下组合:
- Qt 5.15 LTS(长期支持版本)或Qt 6.2+
- Visual Studio 2019/2022(社区版即可)
注意:Qt 5和6在部分API上有差异,新项目建议直接使用Qt 6,除非有必须使用Qt 5的第三方库依赖。
2.2 Qt VS Tools插件安装
这是连接两个环境的关键组件:
- 在VS中打开"扩展"→"管理扩展"
- 搜索"Qt Visual Studio Tools"
- 安装后重启VS
- 在Qt VS Tools的设置中配置Qt安装路径
2.3 项目属性配置
在VS中创建Qt项目后,需要检查以下关键配置:
- C++语言标准:建议至少C++17
- Qt模块选择:根据需求勾选Core、Gui、Widgets等
- 输出目录设置:建议使用相对路径如
$(SolutionDir)bin\$(Platform)\$(Configuration)\
3. 项目结构设计与实现
3.1 推荐的项目目录结构
code复制ProjectRoot/
├── bin/ # 可执行文件
├── include/ # 公共头文件
├── lib/ # 第三方库
├── src/ # 源代码
│ ├── core/ # 业务逻辑
│ ├── ui/ # 界面相关
│ └── main.cpp
├── resources/ # 资源文件
│ ├── qss/ # 样式表
│ └── images/ # 图片资源
└── CMakeLists.txt # 或.pro文件
3.2 UI与逻辑分离的实现
在Qt项目中,我习惯采用MVP(Model-View-Presenter)模式:
- View层:纯UI,继承自QWidget或QMainWindow
cpp复制// MainWindow.h
#pragma once
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
};
- Presenter层:处理业务逻辑
cpp复制// MainPresenter.h
#pragma once
#include <QObject>
class MainPresenter : public QObject {
Q_OBJECT
public:
explicit MainPresenter(QObject *parent = nullptr);
public slots:
void onButtonClicked();
};
- Model层:数据存取
cpp复制// DataModel.h
#pragma once
#include <QObject>
class DataModel : public QObject {
Q_OBJECT
public:
explicit DataModel(QObject *parent = nullptr);
QString fetchData() const;
};
3.3 QSS样式应用实践
QSS文件示例(resources/qss/style.qss):
qss复制/* 主窗口样式 */
QMainWindow {
background-color: #f0f0f0;
font-family: "Microsoft YaHei";
}
/* 按钮基础样式 */
QPushButton {
min-width: 80px;
min-height: 30px;
border: 1px solid #ccc;
border-radius: 4px;
padding: 5px;
}
/* 按钮悬停效果 */
QPushButton:hover {
background-color: #e0e0e0;
}
/* 特定按钮样式 */
#specialButton {
background-color: #4CAF50;
color: white;
}
在代码中加载QSS:
cpp复制void applyGlobalStyle(QApplication &app) {
QFile styleFile(":/resources/qss/style.qss");
if(styleFile.open(QIODevice::ReadOnly)) {
QString styleSheet = QLatin1String(styleFile.readAll());
app.setStyleSheet(styleSheet);
styleFile.close();
}
}
4. 开发中的常见问题与解决方案
4.1 中文乱码问题
在VS+Qt环境中,中文处理需要特别注意:
- 源文件保存为UTF-8 with BOM格式
- 在main.cpp中添加:
cpp复制#include <QTextCodec>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
// ...
}
4.2 调试信息输出
推荐使用Qt的调试宏:
cpp复制#include <QDebug>
void someFunction() {
qDebug() << "Debug message:" << someVariable;
qWarning() << "This is a warning";
qCritical() << "Critical error occurred!";
}
在VS的输出窗口中查看这些信息,需要在项目属性中启用"Qt Messaging"。
4.3 内存泄漏检测
虽然VS自带内存检测工具,但在Qt环境中更推荐使用:
cpp复制#include <QDebug>
#include <crtdbg.h>
#ifdef _DEBUG
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif
int main(int argc, char *argv[]) {
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
// ...
}
5. 高级技巧与性能优化
5.1 多线程处理
Qt提供了多种线程处理方式,最常用的是QThread和QtConcurrent:
cpp复制// 使用QThread
class Worker : public QObject {
Q_OBJECT
public slots:
void doWork() {
// 耗时操作
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
// 在主线程中
QThread *thread = new QThread;
Worker *worker = new Worker;
worker->moveToThread(thread);
connect(thread, &QThread::started, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &MainWindow::handleResult);
thread->start();
5.2 界面性能优化
- 避免在UI线程进行耗时操作:任何超过50ms的操作都应放到工作线程
- 使用QGraphicsView代替大量独立控件:对于复杂界面,使用Graphics View框架
- 启用OpenGL加速:
cpp复制QApplication::setAttribute(Qt::AA_UseOpenGLES);
QApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
5.3 插件化架构设计
Qt的插件系统可以很好地支持模块化开发:
- 定义接口(plugin_interface.h):
cpp复制class PluginInterface {
public:
virtual ~PluginInterface() {}
virtual QString name() const = 0;
virtual void execute() = 0;
};
Q_DECLARE_INTERFACE(PluginInterface, "com.example.PluginInterface/1.0")
- 实现插件:
cpp复制class MyPlugin : public QObject, public PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "com.example.PluginInterface")
public:
QString name() const override { return "MyPlugin"; }
void execute() override { qDebug() << "Plugin executed"; }
};
- 加载插件:
cpp复制void loadPlugins() {
QDir pluginsDir(qApp->applicationDirPath() + "/plugins");
foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
QObject *plugin = loader.instance();
if (plugin) {
PluginInterface *pluginInterface = qobject_cast<PluginInterface*>(plugin);
if (pluginInterface) {
m_plugins.append(pluginInterface);
}
}
}
}
6. 部署与发布
6.1 静态编译与动态链接
- 静态编译:生成单一可执行文件,但体积较大
bash复制configure -static -prefix "C:\Qt\Static" -opensource -confirm-license -release
- 动态链接:需要附带Qt库,可使用windeployqt工具自动收集依赖:
bash复制windeployqt --release myapp.exe
6.2 创建安装包
推荐使用以下工具之一:
- Inno Setup
- NSIS
- Qt Installer Framework
以Qt Installer Framework为例:
- 创建package目录结构
- 编写config.xml和package.xml
- 使用binarycreator生成安装程序
6.3 多语言支持
Qt提供了完善的国际化支持:
- 在代码中使用tr()标记可翻译文本:
cpp复制QString text = tr("Hello World");
- 使用lupdate生成.ts文件:
bash复制lupdate myproject.pro -ts translation_zh.ts
-
使用Qt Linguist翻译文本
-
使用lrelease生成.qm文件:
bash复制lrelease translation_zh.ts
- 在程序中加载翻译文件:
cpp复制QTranslator translator;
translator.load(":/translations/translation_zh.qm");
qApp->installTranslator(&translator);
7. 实际开发中的经验分享
7.1 信号槽连接的几种方式
- 传统连接方式:
cpp复制connect(sender, SIGNAL(valueChanged(int)), receiver, SLOT(updateValue(int)));
- 新式连接(编译时检查):
cpp复制connect(sender, &Sender::valueChanged, receiver, &Receiver::updateValue);
- Lambda表达式连接:
cpp复制connect(button, &QPushButton::clicked, [=](){
qDebug() << "Button clicked";
});
提示:新项目应优先使用新式连接,它提供了更好的类型安全和编译时检查。
7.2 自定义控件的实现
创建继承自QWidget的自定义控件:
cpp复制class CircleProgress : public QWidget {
Q_OBJECT
Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)
public:
explicit CircleProgress(QWidget *parent = nullptr);
int value() const { return m_value; }
void setValue(int val);
protected:
void paintEvent(QPaintEvent *) override;
signals:
void valueChanged(int);
private:
int m_value = 0;
};
// 实现
void CircleProgress::paintEvent(QPaintEvent *) {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
// 绘制背景
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(200, 200, 200));
painter.drawEllipse(rect());
// 绘制进度
painter.setBrush(QColor(100, 150, 250));
painter.drawPie(rect(), 90 * 16, -m_value * 16 * 360 / 100);
}
7.3 处理高DPI显示
现代显示器DPI差异大,需要特别处理:
- 启用高DPI支持:
cpp复制QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
- 在QSS中使用相对单位:
qss复制QPushButton {
min-width: 5em; /* 使用em而不是px */
padding: 0.5em;
}
- 加载图片时考虑设备像素比:
cpp复制QPixmap loadScaledPixmap(const QString &path) {
QPixmap pixmap(path);
pixmap.setDevicePixelRatio(qApp->devicePixelRatio());
return pixmap;
}
8. 调试与性能分析工具
8.1 VS内置调试技巧
- 条件断点:右键点击断点→条件
- 数据断点:调试→窗口→断点→新建→数据断点
- 内存查看:调试→窗口→内存
- 反汇编视图:调试→窗口→反汇编
8.2 Qt Creator的调试功能
虽然主要开发在VS中进行,但Qt Creator的某些调试功能也很实用:
- QML调试器
- 信号槽连接可视化
- 内存分析工具
8.3 性能分析工具
- VS性能分析器:调试→性能分析器
- QElapsedTimer:简单的代码段计时
cpp复制QElapsedTimer timer;
timer.start();
// 要测量的代码
qDebug() << "Elapsed:" << timer.elapsed() << "ms";
- 第三方工具:
- VerySleepy (Windows)
- Hotspot (Linux)
- Instruments (macOS)
9. 与现代C++特性的结合
9.1 智能指针的使用
Qt有自己的对象父子关系内存管理,但也可以结合标准库智能指针:
cpp复制#include <memory>
// 独占所有权
std::unique_ptr<QWidget> widget = std::make_unique<QWidget>();
// 共享所有权
std::shared_ptr<Worker> worker = std::make_shared<Worker>();
注意:QObject派生类通常不使用智能指针,而是依赖Qt的对象树机制。
9.2 Lambda表达式与Qt
Lambda在Qt中非常有用,特别是在信号槽连接中:
cpp复制connect(button, &QPushButton::clicked, [this]() {
if (m_worker && m_worker->isRunning()) {
m_worker->requestInterruption();
}
});
9.3 基于范围的for循环
Qt容器支持C++11的范围for循环:
cpp复制QList<QString> names = {"Alice", "Bob", "Charlie"};
for (const auto &name : names) {
qDebug() << name;
}
10. 跨平台开发注意事项
10.1 文件路径处理
使用Qt的路径处理函数而非平台特定代码:
cpp复制QString configPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
QString filePath = QDir(configPath).filePath("settings.ini");
10.2 平台特定代码隔离
使用预定义宏隔离平台相关代码:
cpp复制#ifdef Q_OS_WIN
// Windows特定代码
#elif defined(Q_OS_MACOS)
// macOS特定代码
#elif defined(Q_OS_LINUX)
// Linux特定代码
#endif
10.3 打包发布差异
不同平台的发布方式:
- Windows:windeployqt + Inno Setup
- macOS:macdeployqt + 创建.dmg
- Linux:创建AppImage或Snap包
11. 第三方库集成
11.1 使用vcpkg管理依赖
vcpkg与VS和Qt都能很好集成:
- 安装vcpkg
bash复制git clone https://github.com/microsoft/vcpkg
.\vcpkg\bootstrap-vcpkg.bat
- 安装库
bash复制vcpkg install zlib:x64-windows
- 在VS项目中引用
11.2 CMake项目配置
现代Qt项目推荐使用CMake:
cmake复制cmake_minimum_required(VERSION 3.5)
project(MyApp LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
qt_add_executable(MyApp
src/main.cpp
src/MainWindow.cpp
)
target_link_libraries(MyApp PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Widgets
)
11.3 常见问题处理
- 库版本冲突:确保所有第三方库使用相同的运行时库(MD/MDd)
- 符号导出:正确使用
__declspec(dllexport/dllimport) - 调试信息:确保PDB文件与DLL匹配
12. 持续集成与自动化测试
12.1 单元测试框架
Qt Test是一个轻量级的单元测试框架:
cpp复制#include <QtTest>
class TestMath : public QObject {
Q_OBJECT
private slots:
void testAddition() {
QCOMPARE(1 + 1, 2);
}
void testString_data() {
QTest::addColumn<QString>("string");
QTest::addColumn<bool>("result");
QTest::newRow("empty") << "" << false;
QTest::newRow("valid") << "hello" << true;
}
void testString() {
QFETCH(QString, string);
QFETCH(bool, result);
QCOMPARE(!string.isEmpty(), result);
}
};
QTEST_APPLESS_MAIN(TestMath)
12.2 CI/CD配置示例
GitHub Actions配置示例:
yaml复制name: CI
on: [push, pull_request]
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: Setup Qt
uses: jurplel/install-qt-action@v2
with:
version: '6.2.0'
- name: Configure
run: cmake -B build -DCMAKE_BUILD_TYPE=Release
- name: Build
run: cmake --build build --config Release
- name: Test
run: cd build && ctest -C Release
13. 界面设计进阶技巧
13.1 使用Qt Designer
-
自定义控件集成:
- 创建插件项目
- 实现
QDesignerCustomWidgetInterface - 将生成的DLL放入Qt Designer的插件目录
-
动态UI加载:
cpp复制QUiLoader loader;
QFile file(":/forms/dynamic.ui");
file.open(QFile::ReadOnly);
QWidget *widget = loader.load(&file);
file.close();
13.2 动画效果实现
Qt提供了强大的动画框架:
cpp复制QPropertyAnimation *animation = new QPropertyAnimation(button, "geometry");
animation->setDuration(1000);
animation->setStartValue(QRect(0, 0, 100, 30));
animation->setEndValue(QRect(100, 100, 200, 60));
animation->setEasingCurve(QEasingCurve::OutBounce);
animation->start();
13.3 3D图形集成
使用Qt 3D模块:
cpp复制#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DExtras/QPhongMaterial>
#include <Qt3DCore/QEntity>
Qt3DExtras::Qt3DWindow view;
Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity;
// 创建球体
Qt3DExtras::QSphereMesh *sphere = new Qt3DExtras::QSphereMesh;
sphere->setRadius(3);
Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial;
material->setDiffuse(QColor(QRgb(0x665423)));
Qt3DCore::QEntity *sphereEntity = new Qt3DCore::QEntity(rootEntity);
sphereEntity->addComponent(sphere);
sphereEntity->addComponent(material);
view.setRootEntity(rootEntity);
view.show();
14. 项目文档与维护
14.1 Doxygen文档生成
配置示例(Doxyfile):
code复制PROJECT_NAME = "MyApp"
INPUT = src include
RECURSIVE = YES
FILE_PATTERNS = *.h *.cpp
OUTPUT_DIRECTORY = docs
GENERATE_LATEX = NO
GENERATE_HTML = YES
QT_AUTOBRIEF = YES
JAVADOC_AUTOBRIEF = YES
EXTRACT_ALL = YES
EXTRACT_PRIVATE = YES
EXTRACT_STATIC = YES
14.2 版本控制策略
推荐的分支模型:
main:稳定发布版本develop:集成开发分支feature/*:功能开发分支release/*:发布准备分支hotfix/*:紧急修复分支
14.3 代码审查要点
-
内存管理:
- 检查new/delete配对
- 确认QObject父子关系正确
- 避免循环引用
-
线程安全:
- 检查跨线程信号槽连接
- 确认共享数据保护
- 避免在非UI线程操作UI
-
性能敏感区域:
- 频繁调用的槽函数
- 大数据量处理
- 界面重绘区域
15. 项目发布与后续维护
15.1 版本号管理
推荐语义化版本控制(SemVer):
- MAJOR:不兼容的API修改
- MINOR:向下兼容的功能新增
- PATCH:向下兼容的问题修正
在代码中定义版本:
cpp复制#define APP_VERSION_MAJOR 1
#define APP_VERSION_MINOR 0
#define APP_VERSION_PATCH 0
QString versionString() {
return QString("%1.%2.%3").arg(APP_VERSION_MAJOR)
.arg(APP_VERSION_MINOR)
.arg(APP_VERSION_PATCH);
}
15.2 错误报告系统
实现简单的错误收集:
cpp复制void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
QFile logFile("error.log");
if (logFile.open(QIODevice::Append)) {
QTextStream stream(&logFile);
stream << QDateTime::currentDateTime().toString() << " - " << msg << "\n";
logFile.close();
}
// 同时输出到标准错误
fprintf(stderr, "%s\n", msg.toLocal8Bit().constData());
}
int main(int argc, char *argv[]) {
qInstallMessageHandler(messageHandler);
// ...
}
15.3 自动更新机制
简单的更新检查实现:
cpp复制void checkForUpdates() {
QNetworkAccessManager manager;
QNetworkRequest request(QUrl("https://example.com/api/version"));
QNetworkReply *reply = manager.get(request);
QEventLoop loop;
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
if (reply->error() == QNetworkReply::NoError) {
QString latestVersion = reply->readAll();
if (latestVersion > versionString()) {
QMessageBox::information(nullptr, tr("Update"),
tr("New version %1 is available").arg(latestVersion));
}
}
reply->deleteLater();
}
16. 实际项目经验分享
16.1 大型项目架构建议
对于超过10万行代码的项目:
- 模块化设计:将功能分解为独立的动态库
- 插件系统:使用Qt插件机制实现功能扩展
- 接口抽象:定义清晰的模块间接口
- 分层架构:严格分离UI、业务逻辑和数据访问层
16.2 团队协作规范
-
编码风格:
- 使用.clang-format统一格式
- 遵循Qt命名约定(类名大写开头,变量小写开头)
-
提交规范:
- 使用约定式提交(Conventional Commits)
- 每个提交只做一件事
- 提交信息说明"为什么"而非"做了什么"
-
文档要求:
- 公共接口必须有Doxygen注释
- 复杂算法需要流程图或伪代码说明
- 每个模块有README.md说明其职责
16.3 性能关键代码优化
- 减少信号槽连接:高频信号考虑批量处理
- 避免频繁界面更新:使用
QWidget::update()而非repaint() - 预计算与缓存:对于不变的计算结果进行缓存
- 使用高效容器:
- QVector而非QList(Qt 6中QList已优化)
- QHash而非QMap(当不需要排序时)
17. 现代Qt开发趋势
17.1 QML与C++混合编程
虽然本项目使用Widgets,但了解QML也很重要:
qml复制// Main.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
visible: true
width: 400
height: 300
Button {
text: "Click Me"
anchors.centerIn: parent
onClicked: backend.handleClick()
}
}
C++后端注册:
cpp复制#include <QQmlApplicationEngine>
#include <QQuickStyle>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QQuickStyle::setStyle("Material");
qmlRegisterType<Backend>("com.example.backend", 1, 0, "Backend");
QQmlApplicationEngine engine;
engine.load(QUrl("qrc:/main.qml"));
return app.exec();
}
17.2 Qt for Python (PySide6)
Python开发者也可以使用Qt:
python复制from PySide6.QtWidgets import QApplication, QPushButton
app = QApplication([])
button = QPushButton("Hello")
button.clicked.connect(lambda: print("Clicked!"))
button.show()
app.exec()
17.3 移动端开发
Qt也支持iOS和Android开发,关键考虑:
- 触摸交互设计
- 不同屏幕尺寸适配
- 平台特定功能集成
- 性能优化(移动设备资源有限)
18. 调试复杂问题的技巧
18.1 死锁排查
在多线程Qt应用中,死锁是常见问题:
- 识别症状:界面无响应但CPU使用率低
- 获取调用栈:
- VS调试器中暂停执行
- 查看所有线程的调用栈
- 常见原因:
- 互斥锁的嵌套获取
- 信号槽连接导致的间接锁获取
- QObject在不同线程间移动时的竞争条件
18.2 内存泄漏定位
除了常规工具,Qt提供了一些特殊机制:
cpp复制// 在main.cpp中
#ifdef QT_DEBUG
#include <QDebug>
#include <QThreadStorage>
static QThreadStorage<int*> dummy;
#endif
int main(int argc, char *argv[]) {
#ifdef QT_DEBUG
qDebug() << "Debug build - memory leak checks enabled";
#endif
// ...
}
18.3 界面渲染问题
当遇到奇怪的渲染问题时:
- 检查
QApplication::setAttribute设置 - 确认图形驱动是否正常
- 尝试不同的渲染后端:
cpp复制QQuickWindow::setSceneGraphBackend("software");
// 或 "opengl", "direct3d", "metal"等
19. 项目迁移与升级
19.1 Qt 4到Qt 5/6迁移
关键变化点:
- 废弃的API:
QDesktopWidget→QScreenQTextCodec→ UTF-8 everywhere
- 新的构建系统:qmake → CMake
- 模块重组:
QtWebKit→QtWebEngineQtScript→ QML/JavaScript
19.2 32位到64位迁移
注意事项:
- 检查所有
int与指针的转换 - 更新第三方库到64位版本
- 测试与外部32位进程的交互
19.3 跨编译器兼容性
确保代码能在不同编译器(MSVC、GCC、Clang)下编译:
- 避免编译器特定扩展
- 处理不同编译器对C++标准的实现差异
- 使用
Q_COMPILER_XXX宏处理特殊情况
20. 项目收尾与总结
在完成一个Qt+VS项目后,建议进行以下收尾工作:
- 代码审查:特别关注资源管理和线程安全
- 性能分析:使用工具识别瓶颈
- 文档整理:更新所有设计文档和API文档
- 创建示例项目:展示核心功能的实现方式
- 编写测试用例:确保关键路径被覆盖
在实际开发中,我发现以下几点特别重要:
- 保持UI线程的响应性,任何耗时操作都应放到工作线程
- 合理使用Qt的信号槽机制,避免过度连接导致的复杂依赖
- 早期考虑国际化需求,所有用户可见文本使用
tr() - 定期进行性能测试,特别是在添加新功能后
Qt与VS的组合提供了强大的开发能力,但也需要开发者理解两者的特性与限制。通过良好的架构设计和编码规范,可以构建出既美观又高效的桌面应用程序。
