001 《Wt Framework:权威指南》


作者Lou Xiao, gemini创建时间2025-04-07 20:51:09更新时间2025-04-07 20:51:09

备注:Gemini 2.0 Flash Thinking 创作的书籍,用来辅助学习。

书籍大纲

▮▮▮▮ chapter 1: 初识 Wt (Web Toolkit)
▮▮▮▮▮▮▮ 1.1 Wt 框架概述
▮▮▮▮▮▮▮ 1.2 Wt 的特点与优势
▮▮▮▮▮▮▮ 1.3 Wt 应用场景
▮▮▮▮▮▮▮ 1.4 Wt 环境搭建与准备
▮▮▮▮▮▮▮ 1.5 第一个 Wt 应用:Hello, World!
▮▮▮▮ chapter 2: Wt 软件架构与核心概念
▮▮▮▮▮▮▮ 2.1 Wt 框架的整体架构
▮▮▮▮▮▮▮ 2.2 事件驱动模型
▮▮▮▮▮▮▮ 2.3 信号与槽 (Signals and Slots)
▮▮▮▮▮▮▮ 2.4 会话管理 (Session Management)
▮▮▮▮▮▮▮ 2.5 请求处理流程 (Request Handling)
▮▮▮▮ chapter 3: Wt 基础组件 (Widgets) 详解
▮▮▮▮▮▮▮ 3.1 布局管理 (Layout Management)
▮▮▮▮▮▮▮ 3.2 基本表单组件 (Form Widgets)
▮▮▮▮▮▮▮ 3.3 文本与显示组件 (Text and Display Widgets)
▮▮▮▮▮▮▮ 3.4 按钮与链接组件 (Button and Link Widgets)
▮▮▮▮▮▮▮ 3.5 容器组件 (Container Widgets)
▮▮▮▮ chapter 4: Wt 高级组件与特性
▮▮▮▮▮▮▮ 4.1 表格组件 (Table Widgets)
▮▮▮▮▮▮▮ 4.2 树形组件 (Tree Widgets)
▮▮▮▮▮▮▮ 4.3 对话框与窗口 (Dialogs and Windows)
▮▮▮▮▮▮▮ 4.4 图形与绘图 (Graphics and Drawing)
▮▮▮▮▮▮▮ 4.5 拖放操作 (Drag and Drop)
▮▮▮▮ chapter 5: Wt 数据处理与模型 (Data Handling and Models)
▮▮▮▮▮▮▮ 5.1 模型-视图-控制器 (MVC) 架构
▮▮▮▮▮▮▮ 5.2 数据模型 (Data Models)
▮▮▮▮▮▮▮ 5.3 数据绑定 (Data Binding)
▮▮▮▮▮▮▮ 5.4 数据验证 (Data Validation)
▮▮▮▮▮▮▮ 5.5 数据库集成 (Database Integration)
▮▮▮▮ chapter 6: Wt 客户端-服务端通信 (Client-Server Communication)
▮▮▮▮▮▮▮ 6.1 AJAX 与异步请求 (AJAX and Asynchronous Requests)
▮▮▮▮▮▮▮ 6.2 WebSocket 支持
▮▮▮▮▮▮▮ 6.3 服务器推送 (Server Push)
▮▮▮▮▮▮▮ 6.4 文件上传与下载 (File Upload and Download)
▮▮▮▮ chapter 7: Wt 样式与主题 (Styling and Themes)
▮▮▮▮▮▮▮ 7.1 CSS 样式表集成
▮▮▮▮▮▮▮ 7.2 主题 (Themes) 的使用与定制
▮▮▮▮▮▮▮ 7.3 响应式设计 (Responsive Design)
▮▮▮▮ chapter 8: Wt 国际化与本地化 (Internationalization and Localization)
▮▮▮▮▮▮▮ 8.1 多语言支持 (Multi-language Support)
▮▮▮▮▮▮▮ 8.2 本地化资源管理 (Localization Resource Management)
▮▮▮▮ chapter 9: Wt 部署与性能优化 (Deployment and Performance Optimization)
▮▮▮▮▮▮▮ 9.1 服务器部署 (Server Deployment)
▮▮▮▮▮▮▮ 9.2 性能优化策略 (Performance Optimization Strategies)
▮▮▮▮▮▮▮ 9.3 负载均衡 (Load Balancing)
▮▮▮▮ chapter 10: Wt 实战案例分析 (Case Studies)
▮▮▮▮▮▮▮ 10.1 案例一:企业级数据管理平台
▮▮▮▮▮▮▮ 10.2 案例二:在线协作工具
▮▮▮▮▮▮▮ 10.3 案例三:实时监控系统
▮▮▮▮ chapter 11: Wt 扩展与高级话题
▮▮▮▮▮▮▮ 11.1 自定义组件开发 (Custom Widget Development)
▮▮▮▮▮▮▮ 11.2 Wt 与第三方库集成
▮▮▮▮▮▮▮ 11.3 Wt 的未来发展趋势
▮▮▮▮ chapter 12: 附录:Wt 常用 API 速查手册


1. chapter 1:初识 Wt (Web Toolkit)

1.1 Wt 框架概述

Wt (Web Toolkit) 是一个使用 C++ 语言编写的,用于开发 Web 应用的框架。与其他 Web 框架不同,Wt 独树一帜地采用了服务端渲染 (Server-Side Rendering, SSR) 的架构,并以 Widget (组件) 为核心构建用户界面。这意味着 Wt 应用的界面逻辑和状态都运行在服务器端,客户端(通常是浏览器)主要负责展示服务器发送过来的渲染结果以及响应用户输入事件。

Wt 框架的设计目标是构建高性能 (High Performance)高可靠性 (High Reliability)富客户端 (Rich Client) 的 Web 应用。它充分利用了 C++ 语言的优势,提供了强大的类型安全、内存管理和性能优化能力。同时,Wt 抽象了底层的 Web 技术细节,例如 HTML, CSS, JavaScript 等,允许开发者使用熟悉的 C++ 语言和面向对象的设计模式来构建复杂的 Web 界面。

1.2 Wt 的特点与优势

Wt 框架之所以在众多 Web 开发框架中脱颖而出,得益于其独特的设计理念和技术优势:

纯 C++ 开发
使用 Wt,开发者可以使用纯粹的 C++ 语言进行 Web 应用的开发,无需深入学习和掌握前端技术栈(如 JavaScript, HTML, CSS)。这对于 C++ 开发者来说,降低了学习成本,提高了开发效率。

服务端渲染 (SSR)
Wt 应用的核心逻辑和 UI 组件都在服务器端运行和渲染。这种架构带来了诸多优势:
▮▮▮▮ⓐ 更高的安全性: 核心逻辑不暴露在客户端,减少了安全风险。
▮▮▮▮ⓑ 更好的性能: 初始页面加载更快,尤其在网络环境较差的情况下。
▮▮▮▮ⓒ 更易于维护: 所有代码逻辑集中在服务端,方便维护和升级。
▮▮▮▮ⓓ 对 SEO 友好: 服务端渲染的内容更容易被搜索引擎爬虫抓取和索引。

组件化开发 (Component-Based Development)
Wt 采用了组件化的开发模式,提供了丰富的内置组件库 (Widget Library),例如按钮 (Button)、文本框 (TextBox)、表格 (Table)、树 (Tree) 等。开发者可以通过组合和定制这些组件来快速构建用户界面。同时,Wt 也支持自定义组件的开发,以满足特定的业务需求。

事件驱动模型 (Event-Driven Model)
Wt 基于事件驱动模型,用户在客户端的操作(例如点击按钮、输入文本)会触发相应的事件,并将事件传递到服务器端进行处理。开发者可以通过信号 (Signals)槽 (Slots) 机制来响应和处理这些事件,实现丰富的交互功能。

跨浏览器兼容性 (Cross-Browser Compatibility)
Wt 框架在设计之初就考虑了跨浏览器兼容性。它会自动处理不同浏览器之间的差异,确保应用在主流浏览器上都能正常运行,开发者无需花费大量精力处理浏览器兼容性问题。

强大的扩展性 (Extensibility)
Wt 提供了丰富的扩展机制,允许开发者集成第三方库,例如数据库 (Database)、图形库 (Graphics Library) 等。同时,Wt 也支持与其他 Web 技术(如 JavaScript)进行集成,以满足更复杂的需求。

1.3 Wt 应用场景

由于其独特的特点和优势,Wt 框架在以下应用场景中表现出色:

企业级 Web 应用 (Enterprise Web Applications)
Wt 的高性能、高可靠性和安全性使其非常适合构建企业级的 Web 应用,例如 CRM (Customer Relationship Management)、ERP (Enterprise Resource Planning)、OA (Office Automation) 系统等。这些系统通常对性能和稳定性有较高要求,并且需要处理大量的业务逻辑和数据。

桌面应用 Web 化 (Webification of Desktop Applications)
对于一些传统的 C++ 桌面应用,如果需要将其迁移到 Web 平台,Wt 是一个理想的选择。Wt 的组件化开发模式和 C++ 语言的亲和性,可以帮助开发者快速将桌面应用的功能和界面移植到 Web 端,实现跨平台访问。

科学计算与数据可视化 (Scientific Computing and Data Visualization)
Wt 结合 C++ 的高性能计算能力,可以用于构建科学计算和数据可视化相关的 Web 应用。例如,在线数据分析平台、科学模型模拟系统、实时数据监控仪表盘等。

嵌入式 Web 界面 (Embedded Web Interfaces)
Wt 的轻量级和高效性使其也适用于嵌入式系统。可以使用 Wt 构建嵌入式设备的 Web 管理界面,例如路由器、交换机、工业控制设备等。通过 Web 界面可以方便地对设备进行配置和监控。

教育与科研 (Education and Research)
Wt 框架的易用性和强大的功能使其在教育和科研领域也具有应用价值。可以用于开发在线教育平台、实验模拟系统、科研数据管理工具等。

1.4 Wt 环境搭建与准备

在开始 Wt 应用开发之前,需要先搭建 Wt 的开发环境。环境搭建过程主要包括以下几个步骤:

安装 C++ 编译器 (C++ Compiler)
由于 Wt 是 C++ 框架,因此需要安装 C++ 编译器。常用的 C++ 编译器包括 GCC (GNU Compiler Collection)、Clang、Visual C++ 等。根据你的操作系统选择合适的编译器进行安装。

安装 CMake (Cross-platform Make)
Wt 使用 CMake 作为构建工具,因此需要安装 CMake。CMake 可以帮助我们管理项目的构建过程,生成不同平台下的构建文件(例如 Makefile, Visual Studio 项目文件)。

下载 Wt 源代码 (Wt Source Code)
从 Wt 官网 https://www.webtoolkit.eu/wt 下载 Wt 框架的源代码。你可以选择下载稳定版本 (Stable Release) 或开发版本 (Development Release)。

编译和安装 Wt (Compile and Install Wt)
解压下载的 Wt 源代码包,进入源代码目录,使用 CMake 进行配置和构建。具体的编译安装步骤可以参考 Wt 官网提供的文档。通常的步骤如下:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 mkdir build
2 cd build
3 cmake ..
4 make
5 sudo make install

注意:具体的 CMake 配置选项和编译参数可能需要根据你的操作系统和需求进行调整。

配置开发环境 (Configure Development Environment)
安装完成后,需要配置开发环境,例如设置环境变量、配置 IDE (Integrated Development Environment) 等。确保编译器和 Wt 库文件能够被正确找到。

1.5 第一个 Wt 应用:Hello, World!

环境搭建完成后,让我们来创建第一个 Wt 应用:Hello, World! 这个简单的示例将帮助你快速了解 Wt 应用的基本结构。

创建项目文件 (Create Project Files)
创建一个新的目录作为项目根目录,例如 wt-hello-world。在该目录下创建一个 C++ 源文件,例如 hello.cpp,以及一个 CMakeLists.txt 文件用于 CMake 构建配置。

编写 C++ 代码 (Write C++ Code)
hello.cpp 文件中,输入以下代码:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WServer.h>
3 #include <Wt/WText.h>
4
5 class HelloApplication : public Wt::WApplication
6 {
7 public:
8 HelloApplication(const Wt::WEnvironment& env) : Wt::WApplication(env)
9 {
10 setTitle("Wt Hello World");
11 root()->addWidget(std::make_unique<Wt::WText>("Hello, World!"));
12 }
13 };
14
15 int main(int argc, char** argv)
16 {
17 Wt::WServer server(argc, argv);
18
19 server.addApplication(server.getContextPath(), [](const Wt::WEnvironment& env) {
20 return std::make_unique<HelloApplication>(env);
21 });
22
23 if (server.start()) {
24 Wt::WServer::waitForShutdown();
25 server.stop();
26 }
27 return 0;
28 }

这段代码创建了一个简单的 Wt 应用,它继承自 Wt::WApplication 类,并在界面上显示 "Hello, World!" 文本。

编写 CMake 配置文件 (Write CMake Configuration File)
CMakeLists.txt 文件中,输入以下内容:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 cmake_minimum_required(VERSION 3.10)
2 project(WtHelloWorld)
3
4 find_package(Wt REQUIRED)
5
6 add_executable(hello hello.cpp)
7 target_link_libraries(hello Wt::Wt)

这个 CMake 配置文件指定了项目名称,查找 Wt 库,并创建一个名为 hello 的可执行文件,链接 Wt 库。

构建和运行应用 (Build and Run Application)
在项目根目录下,创建 build 目录,并使用 CMake 构建项目:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 mkdir build
2 cd build
3 cmake ..
4 make

构建成功后,在 build 目录下会生成可执行文件 hello。运行该程序:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 ./hello

默认情况下,Wt 应用会监听 http://localhost:8080 端口。在浏览器中打开该地址,你将看到 "Hello, World!" 页面。 🎉

2. chapter 2:Wt 软件架构与核心概念

2.1 Wt 框架的整体架构

Wt 框架采用了经典的三层架构思想,并在此基础上进行了优化和创新,以适应 Web 应用开发的特点。从宏观层面来看,Wt 框架的架构可以概括为以下几个主要组成部分:

客户端 (Client)
客户端通常指的是用户的 Web 浏览器。客户端的主要职责是:
▮▮▮▮ⓐ 展示用户界面 (User Interface, UI): 接收并渲染服务器端发送过来的 UI 描述(实际上是高效的指令集,而非传统的 HTML)。
▮▮▮▮ⓑ 处理用户输入 (User Input): 监听用户的操作,例如鼠标点击、键盘输入等,并将这些操作转化为事件发送到服务器端。
▮▮▮▮ⓒ 与服务器通信 (Communication with Server): 通过 HTTP 或 WebSocket 等协议与 Wt 服务器进行双向通信。

Wt 应用服务器 (Wt Application Server)
Wt 应用服务器是 Wt 框架的核心,负责处理所有的业务逻辑和用户交互。它主要包含以下关键组件:
▮▮▮▮ⓐ WServer: Wt 服务器实例,负责监听端口、处理网络请求、管理 Wt 应用实例的生命周期等。WServer 基于高性能的 Web 服务器技术,例如 libev, libevent, ASIO 等。
▮▮▮▮ⓑ WApplication: 代表一个 Wt 应用实例。每个用户会话 (Session) 通常对应一个 WApplication 实例。WApplication 负责管理整个应用的用户界面、状态和事件处理。
▮▮▮▮ⓒ Widget 库 (Widget Library): 提供了丰富的 UI 组件,例如按钮 (WButton)、文本框 (WLineEdit)、表格 (WTable) 等。开发者可以通过组合和定制这些组件来构建用户界面。
▮▮▮▮ⓓ 会话管理器 (Session Manager): 负责管理用户会话,包括会话的创建、销毁、状态维护等。Wt 使用服务端会话管理,将会话数据存储在服务器端,提高了安全性和可靠性。
▮▮▮▮ⓔ 资源管理器 (Resource Manager): 负责管理应用的静态资源,例如 CSS 样式表、JavaScript 脚本、图片、字体文件等。资源管理器可以优化资源加载,提高应用性能。

后端数据存储 (Backend Data Storage)(可选):
根据应用的需求,Wt 应用可以与后端数据存储系统进行集成,例如关系型数据库 (Relational Database, e.g., PostgreSQL, MySQL)、NoSQL 数据库 (e.g., MongoDB, Redis) 等。Wt 提供了数据库连接和数据访问的接口,方便开发者进行数据持久化和管理。

架构图示 (Simplified Architecture Diagram):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 +-----------------------+ +--------------------------+ +-----------------------+
2 | Client | | Wt Application Server | | Backend Data Storage |
3 | (Web Browser) | | | | (Optional) |
4 +-----------------------+ +--------------------------+ +-----------------------+
5 | | |
6 | User Interface | WServer | Database |
7 | Rendering | WApplication | File System |
8 | User Input Events | Widget Library | ... |
9 | | Session Manager | |
10 | | Resource Manager | |
11 | | ... | |
12 | | |
13 +------------------------<----->------------------------<----->-----------------------+
14 HTTP/WebSocket Communication Data Access

关键特点 (Key Characteristics):

  • 服务端渲染 (Server-Side Rendering, SSR):UI 渲染和逻辑处理都在服务器端完成。
  • 组件化 (Component-Based): 使用 Widget 组件构建用户界面。
  • 事件驱动 (Event-Driven): 基于事件和信号槽机制处理用户交互。
  • 状态ful (Stateful): 服务端维护用户会话状态。

2.2 事件驱动模型

Wt 框架的核心交互模式是事件驱动 (Event-Driven) 模型。在传统的 Web 开发中,客户端通常需要发送请求到服务器,服务器处理请求后返回页面或数据。而在 Wt 应用中,客户端与服务器之间保持长连接(通过 AJAX 或 WebSocket),用户在客户端的操作会触发事件,这些事件会被异步地发送到服务器端进行处理。

事件处理流程 (Event Handling Process):

用户操作 (User Action): 用户在客户端(浏览器)进行操作,例如点击按钮、输入文本、选择菜单项等。

客户端事件捕获 (Client-side Event Capture): 浏览器捕获用户的操作,并将其转化为相应的事件,例如 click 事件、input 事件、change 事件等。

事件序列化与传输 (Event Serialization and Transmission): Wt 客户端 JavaScript 代码会将事件信息序列化(通常是 JSON 格式),并通过 HTTP 请求或 WebSocket 消息发送到 Wt 服务器。

服务器端事件接收与反序列化 (Server-side Event Reception and Deserialization): Wt 服务器接收到客户端发送的事件数据,并将其反序列化为 Wt 框架内部的事件对象。

事件派发与处理 (Event Dispatch and Handling): Wt 框架根据事件类型和目标 Widget,将事件派发到相应的事件处理函数或槽 (Slot)。开发者需要在槽函数中编写具体的事件处理逻辑。

UI 更新 (UI Update): 事件处理逻辑可能会导致 UI 状态的改变。Wt 框架会根据状态变化,生成 UI 更新指令,并将这些指令发送回客户端。

客户端 UI 更新渲染 (Client-side UI Update Rendering): 客户端接收到服务器端发送的 UI 更新指令,并根据指令高效地更新页面 DOM (Document Object Model),从而实现界面的动态变化。

优势 (Advantages of Event-Driven Model):

  • 实时性 (Real-time): 用户操作能够被立即响应,提供流畅的交互体验。
  • 高效性 (Efficiency): 只传输必要的事件数据和 UI 更新指令,减少了网络传输量,提高了效率。
  • 异步性 (Asynchronous): 客户端和服务器端的交互是异步的,不会阻塞用户操作。

2.3 信号与槽 (Signals and Slots)

信号 (Signals)槽 (Slots) 是 Wt 框架中实现事件处理和组件间通信的核心机制,它借鉴了 Qt 框架的信号槽机制。信号槽机制提供了一种类型安全、松耦合的方式来连接事件的发布者 (Sender)订阅者 (Receiver)

基本概念 (Basic Concepts):

  • 信号 (Signal): 信号是 Widget 组件发出的一种通知,表示某种事件已经发生。例如,按钮被点击时会发出 clicked() 信号,文本框内容改变时会发出 changed() 信号。信号本身不包含任何处理逻辑,它只是一个事件发生的指示。

  • 槽 (Slot): 槽是一个普通的 C++ 函数或成员函数,用于响应信号。当一个信号被发出时,所有连接到该信号的槽函数都会被自动调用。槽函数中包含了具体的事件处理逻辑。

  • 连接 (Connection): 信号和槽之间需要建立连接才能进行通信。连接通过 connect() 函数来实现,它将一个信号与一个或多个槽函数关联起来。

工作原理 (Working Principle):

信号发射 (Signal Emission): 当某个事件发生时,例如用户点击按钮,按钮组件会发射 clicked() 信号。

信号传播 (Signal Propagation): Wt 框架内部的信号槽机制会将信号传播到所有已连接的槽函数。

槽函数调用 (Slot Function Invocation): 所有连接到该信号的槽函数会被依次调用。槽函数中可以执行各种操作,例如更新 UI、处理数据、调用其他函数等。

示例代码 (Example Code):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WPushButton.h>
3 #include <Wt/WText.h>
4
5 class MyApplication : public Wt::WApplication
6 {
7 public:
8 MyApplication(const Wt::WEnvironment& env) : Wt::WApplication(env)
9 {
10 setTitle("Signals and Slots Example");
11
12 Wt::WPushButton *button = new Wt::WPushButton("Click Me");
13 Wt::WText *text = new Wt::WText("Button not clicked yet.");
14
15 root()->addWidget(button);
16 root()->addWidget(text);
17
18 // 连接按钮的 clicked() 信号到槽函数 onButtonClicked()
19 button->clicked().connect([=]() {
20 text->setText("Button clicked!");
21 });
22 }
23 };

在这个例子中,button->clicked() 返回的是按钮的 clicked 信号对象。connect() 函数将这个信号与一个 lambda 表达式(作为槽函数)连接起来。当按钮被点击时,clicked() 信号被发射,lambda 表达式会被执行,从而更新 text 组件的文本内容。

优势 (Advantages of Signals and Slots):

  • 类型安全 (Type Safety): 信号和槽的连接是类型安全的,编译器会在编译时检查信号和槽的参数类型是否匹配,避免运行时错误。
  • 松耦合 (Loose Coupling): 信号发布者和订阅者之间解耦,发布者不需要知道有哪些订阅者,订阅者也不需要知道信号是如何发布的,降低了组件之间的依赖性。
  • 灵活性 (Flexibility): 一个信号可以连接到多个槽函数,一个槽函数也可以连接到多个信号,提供了灵活的事件处理机制。
  • 易于扩展 (Extensibility): 可以方便地添加新的信号和槽,扩展组件的功能。

2.4 会话管理 (Session Management)

在 Web 应用中,会话 (Session) 用于跟踪用户的状态和数据。由于 HTTP 协议是无状态的,服务器需要一种机制来识别和区分不同的用户请求,并维护用户的会话状态。Wt 框架提供了完善的服务端会话管理 (Server-Side Session Management) 机制。

会话管理机制 (Session Management Mechanism):

会话创建 (Session Creation): 当用户第一次访问 Wt 应用时,Wt 服务器会创建一个新的会话。每个会话都会分配一个唯一的会话 ID (Session ID)。会话 ID 通常通过 Cookie 或 URL 参数的方式传递给客户端。

会话存储 (Session Storage): Wt 框架默认将会话数据存储在服务器内存中。也可以配置使用其他存储方式,例如文件系统、数据库等,以支持分布式会话管理和持久化。

会话状态维护 (Session State Maintenance): 在会话的生命周期内,Wt 服务器会维护用户的会话状态。会话状态可以包括用户登录信息、用户偏好设置、购物车数据等。这些数据存储在服务器端的会话对象中。

会话 ID 传递 (Session ID Passing): 每次客户端向服务器发送请求时,都会携带会话 ID。Wt 服务器通过会话 ID 来识别用户会话,并加载相应的会话状态。

会话超时与销毁 (Session Timeout and Destruction): 为了节省服务器资源,会话通常会设置超时时间。如果用户在一段时间内没有活动,会话会自动超时并被销毁。Wt 框架提供了会话超时的配置选项。当会话销毁时,会话数据会被清理。

Wt 中的会话管理 (Session Management in Wt):

  • Wt::WApplication: 每个 Wt::WApplication 实例都与一个用户会话关联。应用状态和数据通常存储在 WApplication 对象及其成员变量中。
  • Wt::WEnvironment: Wt::WEnvironment 对象包含了当前请求的环境信息,包括会话 ID、客户端 IP 地址、浏览器信息等。可以通过 WApplication::environment() 方法获取 WEnvironment 对象。
  • Session Cookie: Wt 默认使用 Cookie 来存储会话 ID。Cookie 的名称默认为 WTSESSIONID,可以通过配置进行修改。
  • 会话持久化 (Session Persistence): Wt 提供了会话持久化的支持,可以将session数据存储到文件系统或数据库中,以防止服务器重启或故障导致会话数据丢失。

优势 (Advantages of Server-Side Session Management in Wt):

  • 安全性 (Security): 会话数据存储在服务器端,客户端只存储会话 ID,提高了安全性,防止敏感数据泄露。
  • 可靠性 (Reliability): 服务端会话管理更加可靠,即使客户端出现问题,会话状态仍然能够被服务器维护。
  • 灵活性 (Flexibility): 服务器端可以灵活地控制会话的生命周期和存储方式。

2.5 请求处理流程 (Request Handling)

当客户端(浏览器)向 Wt 服务器发送一个 HTTP 请求时,Wt 框架会按照一定的流程来处理这个请求,并生成响应返回给客户端。

请求处理流程 (Request Handling Process):

请求接收 (Request Reception): Wt 服务器的 WServer 组件监听指定的端口,接收客户端发送的 HTTP 请求。

请求解析 (Request Parsing)WServer 解析 HTTP 请求,包括请求方法 (GET, POST, etc.)、URL 路径、请求头 (Headers)、请求参数 (Parameters)、请求体 (Body) 等。

会话查找与加载 (Session Lookup and Loading): Wt 服务器根据请求中携带的会话 ID(通常从 Cookie 或 URL 参数中获取),查找对应的会话。如果找到会话,则加载会话状态。如果找不到会话 ID,则创建一个新的会话。

应用实例查找 (Application Instance Lookup): 根据请求的 URL 路径,WServer 查找对应的 Wt::WApplication 工厂函数。工厂函数负责创建 WApplication 实例。

WApplication 实例创建或获取 (WApplication Instance Creation or Retrieval): 如果请求是会话的第一个请求,则调用工厂函数创建一个新的 WApplication 实例。如果请求是同一个会话的后续请求,则复用已有的 WApplication 实例。

事件处理与 UI 更新 (Event Handling and UI Update): 如果请求中包含了用户事件数据,WApplication 会根据事件数据进行事件处理,并更新 UI 状态。

响应生成 (Response Generation)WApplication 根据当前的 UI 状态,生成响应数据。响应数据通常是一系列高效的 UI 更新指令,而不是完整的 HTML 页面。

响应发送 (Response Sending)WServer 将响应数据封装成 HTTP 响应,并发送回客户端。响应可能包含 UI 更新指令、资源文件、重定向指令等。

会话状态保存 (Session State Saving): 在请求处理完成后,Wt 服务器会将更新后的会话状态保存起来,以便下次请求使用。

关键点 (Key Points):

  • 单页应用 (Single-Page Application, SPA) 模式: Wt 应用通常以 SPA 模式运行,客户端只加载一次页面,后续的 UI 更新通过异步请求和 UI 更新指令来实现,而不是每次都重新加载整个页面。
  • 高效的 UI 更新 (Efficient UI Update): Wt 框架只传输必要的 UI 更新指令,而不是完整的 HTML,减少了网络传输量,提高了性能。
  • 服务端状态管理 (Server-Side State Management): 所有应用状态都维护在服务器端,客户端只负责展示和用户交互。

通过对 Wt 框架架构和核心概念的理解,我们可以更好地掌握 Wt 应用的开发模式和运行机制,为后续深入学习 Wt 框架打下坚实的基础。 😊 接下来,我们将进入第三章,学习 Wt 的基础组件 (Widgets) 的使用。

3. chapter 3:Wt 基础组件 (Widgets) 详解

在 Wt 框架中,组件 (Widget) 是构建用户界面的基本 building blocks。Wt 提供了丰富的内置组件库,涵盖了各种常用的 UI 元素,例如按钮、文本框、标签、表格、树形控件等。这些组件都继承自 Wt::WWidget 类,并具有统一的接口和行为。通过组合和定制这些组件,开发者可以快速构建出功能完善、用户友好的 Web 界面。

3.1 布局管理 (Layout Management)

在构建用户界面时,合理的布局 (Layout) 至关重要。布局决定了组件在界面上的排列方式和空间分配。Wt 框架提供了多种布局管理器 (Layout Managers),帮助开发者灵活地控制组件的布局。

3.1.1 布局管理器类型 (Layout Manager Types)

Wt 提供了以下几种常用的布局管理器:

Wt::WBoxLayout (Box Layout)
盒式布局管理器,将组件按照水平或垂直方向排列成一行或一列。WBoxLayout 可以设置组件之间的间距、对齐方式和伸缩比例,是最常用的布局管理器之一。

▮▮▮▮ⓐ Wt::WHBoxLayout (Horizontal Box Layout): 水平盒式布局,组件从左到右排列。
▮▮▮▮ⓑ Wt::WVBoxLayout (Vertical Box Layout): 垂直盒式布局,组件从上到下排列。

Wt::WGridLayout (Grid Layout)
网格布局管理器,将组件排列成网格状的行列结构。WGridLayout 可以灵活地控制组件在网格中的位置、跨行跨列、以及行列的伸缩比例。适用于构建复杂的表单或面板布局。

Wt::WStackedWidget (Stacked Widget)
堆叠布局管理器,将多个组件堆叠在一起,同一时刻只显示一个组件。可以通过程序控制当前显示的组件,常用于实现选项卡 (Tab) 或向导 (Wizard) 界面。

Wt::WBorderLayout (Border Layout)
边框布局管理器,将容器划分为五个区域:上 (North)、下 (South)、左 (West)、右 (East)、中 (Center)。可以将组件放置在这些区域中,适用于构建具有固定头部、尾部和侧边栏的页面布局。

Wt::WAnchorLayout (Anchor Layout)
锚点布局管理器,允许使用锚点 (Anchor) 将组件固定在容器的边缘或指定位置。适用于需要精确控制组件位置的场景。

Wt::WHtmlLayout (HTML Layout)
HTML 布局管理器,允许使用 HTML 模板来定义布局。可以在 HTML 模板中使用占位符,将 Wt 组件嵌入到 HTML 结构中。适用于需要高度自定义布局或集成现有 HTML 页面的场景。

3.1.2 布局管理器的使用 (Using Layout Managers)

使用布局管理器的一般步骤如下:

创建布局管理器实例 (Create Layout Manager Instance): 根据需要选择合适的布局管理器类型,并创建其实例。例如:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 Wt::WHBoxLayout *hbox = new Wt::WHBoxLayout();

将布局管理器设置到容器组件 (Set Layout Manager to Container Widget): 将布局管理器设置到一个容器组件 (例如 Wt::WContainerWidget)。容器组件将负责使用该布局管理器来管理其子组件的布局。例如:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 Wt::WContainerWidget *container = new Wt::WContainerWidget();
2 container->setLayout(hbox); // 设置水平盒式布局

向布局管理器添加组件 (Add Widgets to Layout Manager): 将需要布局的子组件添加到布局管理器中。例如:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 Wt::WPushButton *button1 = new Wt::WPushButton("Button 1");
2 Wt::WPushButton *button2 = new Wt::WPushButton("Button 2");
3
4 hbox->addWidget(button1);
5 hbox->addWidget(button2);

对于不同的布局管理器,添加组件的方法和参数可能有所不同。例如,WGridLayout 需要指定组件所在的行和列。

3.1.3 布局示例 (Layout Examples)

示例 1:水平盒式布局 (WHBoxLayout)

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 Wt::WHBoxLayout *hbox = new Wt::WHBoxLayout();
2 Wt::WPushButton *button1 = new Wt::WPushButton("Button 1");
3 Wt::WPushButton *button2 = new Wt::WPushButton("Button 2");
4 Wt::WPushButton *button3 = new Wt::WPushButton("Button 3");
5
6 hbox->addWidget(button1);
7 hbox->addWidget(button2);
8 hbox->addWidget(button3);
9
10 root()->setLayout(hbox); // 将水平盒式布局设置为根容器的布局

这段代码创建了一个水平盒式布局,并将三个按钮组件添加到布局中。按钮将水平排列,从左到右依次显示。

示例 2:网格布局 (WGridLayout)

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 Wt::WGridLayout *grid = new Wt::WGridLayout();
2 Wt::WLabel *label1 = new Wt::WLabel("Name:");
3 Wt::WLineEdit *lineEdit1 = new Wt::WLineEdit();
4 Wt::WLabel *label2 = new Wt::WLabel("Email:");
5 Wt::WLineEdit *lineEdit2 = new Wt::WLineEdit();
6
7 grid->addWidget(label1, 0, 0); // 第一行第一列
8 grid->addWidget(lineEdit1, 0, 1); // 第一行第二列
9 grid->addWidget(label2, 1, 0); // 第二行第一列
10 grid->addWidget(lineEdit2, 1, 1); // 第二行第二列
11
12 root()->setLayout(grid); // 将网格布局设置为根容器的布局

这段代码创建了一个网格布局,并将标签和文本框组件添加到网格中,形成一个简单的表单布局。

通过灵活使用各种布局管理器,可以构建出各种复杂和精美的用户界面。

3.2 基本表单组件 (Form Widgets)

表单 (Form) 是 Web 应用中常用的交互元素,用于收集用户输入的数据。Wt 框架提供了丰富的表单组件 (Form Widgets),方便开发者构建各种类型的表单。

3.2.1 常用表单组件 (Common Form Widgets)

Wt::WLineEdit (Line Edit)
单行文本输入框,用于接收用户输入的单行文本。可以设置输入类型(例如文本、密码、数字)、最大长度、占位符等属性。

Wt::WTextArea (Text Area)
多行文本输入框,用于接收用户输入的多行文本。可以设置行数、列数、是否允许调整大小等属性。

Wt::WPushButton (Push Button)
按钮组件,用于触发用户操作。可以设置按钮文本、图标、禁用状态等属性。按钮的 clicked() 信号用于响应点击事件。

Wt::WCheckBox (Check Box)
复选框组件,用于表示二选一的选择状态(选中或未选中)。可以设置复选框文本、初始状态等属性。checkedChanged() 信号用于响应选中状态的改变事件。

Wt::WRadioButton (Radio Button)
单选按钮组件,通常与 Wt::WRadioButtonGroup 配合使用,用于在一组选项中选择一个。可以设置单选按钮文本、组 ID 等属性。checkedChanged() 信号用于响应选中状态的改变事件。

Wt::WComboBox (Combo Box)
下拉列表框组件,用于从预定义的列表中选择一个选项。可以添加列表项、设置默认选项、是否允许编辑等属性。currentIndexChanged() 信号用于响应选项改变事件。

Wt::WSpinBox (Spin Box)
数值微调器组件,用于输入数值。可以设置数值范围、步长、显示格式等属性。valueChanged() 信号用于响应数值改变事件。

Wt::WSlider (Slider)
滑块组件,用于通过拖动滑块选择数值范围内的值。可以设置数值范围、步长、方向等属性。valueChanged() 信号用于响应数值改变事件。

Wt::WFileUpload (File Upload)
文件上传组件,用于允许用户上传文件到服务器。可以设置允许上传的文件类型、最大文件大小等属性。uploaded() 信号用于响应文件上传完成事件。

3.2.2 表单数据处理 (Form Data Handling)

当用户在表单中输入数据并提交表单时,需要获取表单数据并进行处理。Wt 框架提供了方便的方法来获取表单组件的值。

获取表单组件值的方法 (Methods to Get Form Widget Values):

  • WLineEdit::text(): 获取单行文本输入框的文本值 (QString 类型)。
  • WTextArea::text(): 获取多行文本输入框的文本值 (QString 类型)。
  • WCheckBox::isChecked(): 获取复选框的选中状态 (bool 类型,true 表示选中,false 表示未选中)。
  • WRadioButton::isChecked(): 获取单选按钮的选中状态 (bool 类型)。
  • WComboBox::currentText(): 获取下拉列表框当前选中文本 (QString 类型)。
  • WComboBox::currentIndex(): 获取下拉列表框当前选中项的索引 (int 类型)。
  • WSpinBox::value(): 获取数值微调器的数值 (double 类型)。
  • WSlider::value(): 获取滑块的数值 (double 类型)。

示例代码 (Example Code):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WPushButton.h>
3 #include <Wt/WLineEdit.h>
4 #include <Wt/WText.h>
5 #include <Wt/WVBoxLayout.h>
6
7 class FormExample : public Wt::WApplication
8 {
9 public:
10 FormExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
11 {
12 setTitle("Form Example");
13
14 Wt::WVBoxLayout *vbox = new Wt::WVBoxLayout();
15 Wt::WLineEdit *nameEdit = new Wt::WLineEdit();
16 Wt::WPushButton *submitButton = new Wt::WPushButton("Submit");
17 Wt::WText *resultText = new Wt::WText();
18
19 vbox->addWidget(nameEdit);
20 vbox->addWidget(submitButton);
21 vbox->addWidget(resultText);
22
23 root()->setLayout(vbox);
24
25 submitButton->clicked().connect([=]() {
26 Wt::QString name = nameEdit->text();
27 resultText->setText("Hello, " + name + "!");
28 });
29 }
30 };

在这个例子中,当用户点击 "Submit" 按钮时,槽函数会获取 nameEdit 文本框的值,并将其显示在 resultText 组件中。

3.3 文本与显示组件 (Text and Display Widgets)

文本与显示组件 (Text and Display Widgets) 用于在界面上展示文本、图片、HTML 内容等信息。

3.3.1 常用文本与显示组件 (Common Text and Display Widgets)

Wt::WText (Text)
文本组件,用于显示静态文本内容。支持纯文本和 HTML 格式文本。可以设置文本内容、字体、颜色、样式等属性。

Wt::WLabel (Label)
标签组件,类似于 WText,通常用于显示表单字段的标签或提示信息。

Wt::WImage (Image)
图片组件,用于显示图片。可以设置图片源 (URL 或资源路径)、图片大小、alt 文本等属性。支持多种图片格式 (例如 PNG, JPEG, GIF)。

Wt::WLink (Link)
链接组件,用于创建超链接。可以设置链接文本、URL、打开方式 (例如新窗口、当前窗口) 等属性。

Wt::WTemplate (Template)
模板组件,用于显示 HTML 模板内容。可以在 HTML 模板中使用占位符,动态地插入数据或组件。

Wt::WProgressBar (Progress Bar)
进度条组件,用于显示任务的进度。可以设置进度值、进度范围、显示样式等属性。

Wt::WTimer (Timer)
定时器组件,用于定时触发事件。可以设置定时时间间隔、是否重复触发等属性。timeout() 信号用于响应定时器超时事件。注意:WTimer 虽然是组件,但通常不直接显示在界面上,而是在后台运行。

3.3.2 文本格式化 (Text Formatting)

Wt::WTextWt::WLabel 组件支持 HTML 格式的文本内容,可以使用 HTML 标签来格式化文本,例如:

  • <b> 粗体
  • <i> 斜体
  • <u> 下划线
  • <br> 换行
  • <p> 段落
  • <a> 链接
  • <span> 行内容器
  • <div> 块级容器

示例代码 (Example Code):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 Wt::WText *formattedText = new Wt::WText();
2 formattedText->setTextFormat(Wt::TextFormat::XHTML); // 设置文本格式为 XHTML (HTML)
3 formattedText->setText("<b>This is bold text</b>, <i>this is italic</i>, <br> and this is a <a href='https://www.webtoolkit.eu/wt'>link</a>.");
4
5 root()->addWidget(formattedText);

这段代码创建了一个 WText 组件,并使用 HTML 标签格式化文本内容。

3.4 按钮与链接组件 (Button and Link Widgets)

按钮与链接组件 (Button and Link Widgets) 是用户与 Web 应用进行交互的重要入口。

3.4.1 按钮组件 (Button Widgets)

Wt 提供了多种按钮组件,满足不同的需求:

Wt::WPushButton (Push Button): 最常用的按钮组件,普通的按钮。

Wt::WToolButton (Tool Button): 工具栏按钮,通常用于工具栏或菜单栏,外观更简洁。

Wt::WDropDownButton (Drop-Down Button): 下拉按钮,点击按钮时会弹出一个下拉菜单。

Wt::WMenu (Menu): 菜单组件,通常与 WDropDownButtonWt::WPopupMenu 配合使用,创建菜单结构。

Wt::WPopupMenu (Popup Menu): 弹出菜单,通常通过鼠标右键点击或其他手势触发弹出。

按钮的常用信号 (Common Signals of Button Widgets):

  • clicked(): 按钮被点击时发射的信号。
  • doubleClicked(): 按钮被双击时发射的信号。
  • mouseEntered(): 鼠标指针进入按钮区域时发射的信号。
  • mouseLeft(): 鼠标指针离开按钮区域时发射的信号。

3.4.2 链接组件 (Link Widgets)

Wt::WLink (Link) 组件用于创建超链接,可以链接到内部页面、外部 URL 或资源文件。

WLink 的常用属性 (Common Properties of WLink):

  • setUrl(const Wt::WLink& url): 设置链接的目标 URL。
  • setText(const Wt::WString& text): 设置链接的文本内容。
  • setTarget(Target target): 设置链接的打开方式 (例如 Target::NewWindow, Target::CurrentWindow)。

示例代码 (Example Code):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 Wt::WPushButton *button = new Wt::WPushButton("Click Me");
2 Wt::WLink *link = new Wt::WLink(Wt::WLink("https://www.webtoolkit.eu/wt"));
3 link->setText("Go to Wt Website");
4 link->setTarget(Wt::Target::NewWindow); // 在新窗口打开链接
5
6 root()->addWidget(button);
7 root()->addWidget(link);

这段代码创建了一个按钮和一个链接组件。点击链接将会在新窗口打开 Wt 官网。

3.5 容器组件 (Container Widgets)

容器组件 (Container Widgets) 用于组织和管理其他组件。布局管理器通常需要设置到一个容器组件上,才能发挥作用。

3.5.1 常用容器组件 (Common Container Widgets)

Wt::WContainerWidget (Container Widget): 最基本的容器组件,可以容纳其他任何组件。可以设置布局管理器、背景颜色、边框等属性。

Wt::WGroupBox (Group Box): 分组框组件,用于将一组相关的组件组合在一起,并显示一个标题边框。

Wt::WPanel (Panel): 面板组件,类似于 WContainerWidget,但通常具有更丰富的外观样式,例如阴影、圆角等。

Wt::WTabWidget (Tab Widget): 选项卡组件,用于创建选项卡式界面。每个选项卡可以包含一组组件。

Wt::WDockWidget (Dock Widget): 停靠窗口组件,用于创建可停靠、可拖拽的窗口,常用于构建 IDE 或桌面应用风格的界面。

Wt::WScrollArea (Scroll Area): 滚动区域组件,当内容超出容器大小时,可以显示滚动条。

3.5.2 容器组件的嵌套 (Nesting of Container Widgets)

容器组件可以互相嵌套,形成复杂的组件树结构。通过合理地嵌套容器组件和使用布局管理器,可以构建出层次清晰、结构良好的用户界面。

示例代码 (Example Code):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 Wt::WContainerWidget *mainContainer = new Wt::WContainerWidget();
2 Wt::WVBoxLayout *mainLayout = new Wt::WVBoxLayout();
3 mainContainer->setLayout(mainLayout);
4
5 Wt::WGroupBox *groupBox = new Wt::WGroupBox("User Information");
6 Wt::WGridLayout *gridLayout = new Wt::WGridLayout();
7 groupBox->setLayout(gridLayout);
8
9 Wt::WLabel *nameLabel = new Wt::WLabel("Name:");
10 Wt::WLineEdit *nameEdit = new Wt::WLineEdit();
11 Wt::WLabel *emailLabel = new Wt::WLabel("Email:");
12 Wt::WLineEdit *emailEdit = new Wt::WLineEdit();
13
14 gridLayout->addWidget(nameLabel, 0, 0);
15 gridLayout->addWidget(nameEdit, 0, 1);
16 gridLayout->addWidget(emailLabel, 1, 0);
17 gridLayout->addWidget(emailEdit, 1, 1);
18
19 mainLayout->addWidget(groupBox); // 将分组框添加到主布局中
20
21 root()->addWidget(mainContainer); // 将主容器添加到根容器中

这段代码演示了容器组件的嵌套使用。一个 WGroupBox 容器(使用 WGridLayout 布局)嵌套在 WContainerWidget 容器(使用 WVBoxLayout 布局)中,构建了一个简单的用户信息分组框。

掌握了 Wt 框架的基础组件,你就可以开始构建各种各样的 Web 界面了。在接下来的章节中,我们将学习更高级的组件和特性,例如表格、树形控件、对话框、图形绘图等。 😊

4. chapter 4:Wt 高级组件与特性

在前一章中,我们学习了 Wt 的基础组件,它们可以构建简单的用户界面。为了应对更复杂的需求,Wt 框架还提供了一系列高级组件 (Advanced Widgets)特性 (Features),例如表格、树形控件、对话框、图形绘图和拖放操作等。本章将深入探讨这些高级功能,帮助你构建更强大、更丰富的 Web 应用。

4.1 表格组件 (Table Widgets)

表格 (Table) 是用于展示和编辑结构化数据的常用组件。Wt 框架提供了 Wt::WTable 组件,用于创建和操作表格。

4.1.1 Wt::WTable 组件详解

Wt::WTable 组件提供了丰富的功能,包括:

行列操作 (Row and Column Operations)
可以动态添加、删除、插入行和列。

单元格内容 (Cell Content)
每个单元格可以包含任何 Wt 组件,例如文本、图片、输入框、按钮等。

表头 (Headers)
可以设置表头,用于描述列的含义。表头可以是文本或自定义组件。

排序 (Sorting)
支持表格数据排序,可以根据不同的列进行排序。

样式定制 (Styling)
可以定制表格的样式,例如边框、背景颜色、字体等。

事件处理 (Event Handling)
可以响应表格的各种事件,例如单元格点击、行选中等。

4.1.2 Wt::WTable 的使用 (Using Wt::WTable)

创建 Wt::WTable 实例 (Creating Wt::WTable Instance):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 Wt::WTable *table = new Wt::WTable();
2 root()->addWidget(table);

设置表头 (Setting Headers):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 table->setHeaderCount(1); // 设置表头行数为 1
2 table->elementAt(0, 0)->addWidget(std::make_unique<Wt::WText>("Name")); // 第一行第一列表头
3 table->elementAt(0, 1)->addWidget(std::make_unique<Wt::WText>("Age")); // 第一行第二列表头
4 table->elementAt(0, 2)->addWidget(std::make_unique<Wt::WText>("City")); // 第一行第三列表头

添加数据行 (Adding Data Rows):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 int row = table->rowCount(); // 获取当前行数,新行将添加到末尾
2 table->insertRow(row); // 插入新行
3
4 table->elementAt(row, 0)->addWidget(std::make_unique<Wt::WText>("Alice")); // 第一列数据
5 table->elementAt(row, 1)->addWidget(std::make_unique<Wt::WText>("25")); // 第二列数据
6 table->elementAt(row, 2)->addWidget(std::make_unique<Wt::WText>("New York"));// 第三列数据
7
8 row = table->rowCount();
9 table->insertRow(row);
10 table->elementAt(row, 0)->addWidget(std::make_unique<Wt::WText>("Bob"));
11 table->elementAt(row, 1)->addWidget(std::make_unique<Wt::WText>("30"));
12 table->elementAt(row, 2)->addWidget(std::make_unique<Wt::WText>("London"));

定制单元格内容 (Customizing Cell Content):

单元格可以添加任何 Wt 组件,例如输入框,实现表格编辑功能:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 Wt::WLineEdit *nameEdit = new Wt::WLineEdit("Charlie");
2 table->elementAt(2, 0)->addWidget(std::unique_ptr<Wt::WWidget>(nameEdit)); // 第三行第一列添加输入框

表格排序 (Table Sorting):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 table->setSortingEnabled(true); // 启用排序功能
2 table->setColumnHeader(0, "Name", 0); // 设置列头文本和排序的列索引
3 table->setColumnHeader(1, "Age", 1);
4 table->setColumnHeader(2, "City", 2);

表格样式 (Table Styling):

可以使用 CSS 样式表定制表格外观,例如设置边框样式:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 table->setStyleClass("my-table"); // 设置 CSS 类名
2
3 // 在 CSS 文件中定义 .my-table 样式
4 .my-table {
5 border-collapse: collapse; /* 合并边框 */
6 }
7
8 .my-table th, .my-table td {
9 border: 1px solid black; /* 设置单元格边框 */
10 padding: 5px;
11 }

4.1.3 表格事件处理 (Table Event Handling)

Wt::WTable 组件提供了多种信号,用于响应用户在表格上的操作,例如:

  • cellClicked(int row, int column, const WMouseEvent& event): 单元格被点击时发射的信号。
  • rowSelected(int row): 行被选中时发射的信号。
  • columnSorted(int column, SortOrder order): 列被排序时发射的信号。

示例代码:响应单元格点击事件 (Example Code: Handling Cell Clicked Event):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 table->cellClicked().connect([=](int row, int column, const Wt::WMouseEvent& event) {
2 Wt::log("info") << "Cell clicked: Row=" << row << ", Column=" << column;
3 // 在这里处理单元格点击事件
4 });

4.2 树形组件 (Tree Widgets)

树形组件 (Tree Widget) 用于以树状结构展示层次数据。Wt 框架提供了 Wt::WTree 组件,用于创建和操作树形结构。

4.2.1 Wt::WTree 组件详解

Wt::WTree 组件的主要特性包括:

树形结构 (Tree Structure)
以树状层次结构展示数据,节点可以展开或折叠。

节点内容 (Node Content)
每个节点可以包含文本、图标或自定义组件。

节点操作 (Node Operations)
可以动态添加、删除、插入节点。

节点选择 (Node Selection)
支持单选或多选节点。

拖放 (Drag and Drop)
支持节点拖放操作,可以移动节点或在不同树之间拖放节点。

样式定制 (Styling)
可以定制树形控件的样式,例如节点图标、线条样式等。

事件处理 (Event Handling)
可以响应树形控件的各种事件,例如节点点击、节点展开/折叠、节点选择等。

4.2.2 Wt::WTree 的使用 (Using Wt::WTree)

创建 Wt::WTree 实例 (Creating Wt::WTree Instance):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 Wt::WTree *tree = new Wt::WTree();
2 root()->addWidget(tree);

添加根节点 (Adding Root Nodes):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 Wt::WTreeNode *rootNode1 = tree->addItem("Root Node 1");
2 Wt::WTreeNode *rootNode2 = tree->addItem("Root Node 2");

添加子节点 (Adding Child Nodes):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 Wt::WTreeNode *childNode1 = rootNode1->addChild("Child Node 1");
2 Wt::WTreeNode *childNode2 = rootNode1->addChild("Child Node 2");
3 Wt::WTreeNode *grandchildNode = childNode1->addChild("Grandchild Node");

设置节点文本和图标 (Setting Node Text and Icon):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 rootNode1->setText("一级节点 1");
2 rootNode1->setIcon(Wt::WLink("images/folder-icon.png")); // 设置节点图标

节点展开和折叠 (Expanding and Collapsing Nodes):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 rootNode1->setExpanded(true); // 默认展开根节点 1
2 childNode2->setExpanded(false); // 默认折叠子节点 2
3
4 // 程序控制展开或折叠节点
5 if (childNode1->isExpanded()) {
6 childNode1->setExpanded(false); // 折叠节点
7 } else {
8 childNode1->setExpanded(true); // 展开节点
9 }

节点选择 (Node Selection):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 tree->setSelectionMode(Wt::SelectionMode::Single); // 设置单选模式
2 // tree->setSelectionMode(Wt::SelectionMode::Multi); // 设置多选模式
3
4 tree->setSelectable(true); // 启用节点选择功能
5
6 // 获取选中的节点
7 std::vector<Wt::WTreeNode *> selectedNodes = tree->selectedNodes();
8 for (Wt::WTreeNode *node : selectedNodes) {
9 Wt::log("info") << "Selected node: " << node->text();
10 }

4.2.3 树形控件事件处理 (Tree Widget Event Handling)

Wt::WTree 组件提供了多种信号,用于响应用户在树形控件上的操作,例如:

  • itemClicked(Wt::WTreeNode *item, const WMouseEvent& event): 节点被点击时发射的信号。
  • itemExpanded(Wt::WTreeNode *item): 节点被展开时发射的信号。
  • itemCollapsed(Wt::WTreeNode *item): 节点被折叠时发射的信号。
  • itemSelectionChanged(): 节点选择状态改变时发射的信号。

示例代码:响应节点点击事件 (Example Code: Handling Item Clicked Event):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 tree->itemClicked().connect([=](Wt::WTreeNode *item, const Wt::WMouseEvent& event) {
2 Wt::log("info") << "Node clicked: " << item->text();
3 // 在这里处理节点点击事件
4 });

4.3 对话框与窗口 (Dialogs and Windows)

对话框 (Dialog)窗口 (Window) 是用于显示临时信息、提示用户操作或执行特定任务的独立界面元素。Wt 框架提供了 Wt::WDialogWt::WWindow 组件,用于创建对话框和窗口。

4.3.1 Wt::WDialog 组件详解

Wt::WDialog 组件用于创建模态对话框,通常用于:

信息提示 (Information Messages): 显示提示信息、警告信息、错误信息等。

用户确认 (User Confirmation): 请求用户确认操作,例如 "确定要删除吗?"。

简单输入 (Simple Input): 获取用户简单的输入,例如文件名、密码等。

Wt::WDialog 是模态的,当对话框显示时,用户无法与主窗口进行交互,必须先关闭对话框才能继续操作。

Wt::WDialog 的使用 (Using Wt::WDialog):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 Wt::WDialog *dialog = new Wt::WDialog("Confirmation");
2 dialog->setModal(true); // 设置为模态对话框
3
4 Wt::WText *message = new Wt::WText("Are you sure you want to delete?");
5 Wt::WPushButton *okButton = new Wt::WPushButton("OK");
6 Wt::WPushButton *cancelButton = new Wt::WPushButton("Cancel");
7
8 Wt::WVBoxLayout *dialogLayout = new Wt::WVBoxLayout();
9 dialogLayout->addWidget(message);
10 Wt::WHBoxLayout *buttonLayout = new Wt::WHBoxLayout();
11 buttonLayout->addWidget(okButton);
12 buttonLayout->addWidget(cancelButton);
13 dialogLayout->addLayout(buttonLayout);
14
15 dialog->contents()->setLayout(dialogLayout); // 设置对话框内容布局
16
17 okButton->clicked().connect([=]() {
18 Wt::log("info") << "OK button clicked";
19 dialog->accept(); // 关闭对话框,并返回 DialogCode::Accepted 结果
20 });
21
22 cancelButton->clicked().connect([=]() {
23 Wt::log("info") << "Cancel button clicked";
24 dialog->reject(); // 关闭对话框,并返回 DialogCode::Rejected 结果
25 });
26
27 dialog->show(); // 显示对话框
28
29 dialog->finished().connect([=](Wt::Dialog::DialogCode result) {
30 if (result == Wt::Dialog::Accepted) {
31 // 用户点击了 OK 按钮
32 Wt::log("info") << "Dialog result: Accepted";
33 // 执行删除操作
34 } else {
35 // 用户点击了 Cancel 按钮或关闭按钮
36 Wt::log("info") << "Dialog result: Rejected";
37 // 取消删除操作
38 }
39 delete dialog; // 销毁对话框
40 });

4.3.2 Wt::WWindow 组件详解

Wt::WWindow 组件用于创建独立的非模态窗口,可以用于:

多文档界面 (Multiple Document Interface, MDI): 创建可以同时打开多个文档的界面。

辅助窗口 (Secondary Windows): 创建辅助功能的窗口,例如属性面板、工具窗口等。

Wt::WWindow 是非模态的,当窗口显示时,用户仍然可以与主窗口和其他窗口进行交互。

Wt::WWindow 的使用 (Using Wt::WWindow):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 Wt::WWindow *window = new Wt::WWindow("Secondary Window");
2 window->setMinimumSize({300, 200}); // 设置窗口最小尺寸
3 window->setPosition(Wt::WPoint(100, 100)); // 设置窗口初始位置
4
5 Wt::WText *windowContent = new Wt::WText("This is a secondary window.");
6 window->setCentralWidget(windowContent); // 设置窗口中心组件
7
8 window->show(); // 显示窗口
9
10 window->closed().connect([=]() {
11 Wt::log("info") << "Window closed";
12 delete window; // 销毁窗口
13 });

4.4 图形与绘图 (Graphics and Drawing)

Wt 框架提供了基本的图形与绘图 (Graphics and Drawing) 功能,可以使用 Wt::WPaintDeviceWt::WPainter 类进行简单的 2D 绘图。

4.4.1 Wt::WPaintDeviceWt::WPainter

  • Wt::WPaintDevice: 绘图设备抽象类,表示可以进行绘图的表面。常用的 WPaintDevice 包括 Wt::WImage (用于在图片上绘图) 和 Wt::WPaintedWidget (自定义绘图组件)。
  • Wt::WPainter: 绘图器类,用于在 WPaintDevice 上进行实际的绘图操作。提供了绘制直线、矩形、圆形、文本、图片等基本图形的方法。

4.4.2 绘图示例 (Drawing Example)

自定义绘图组件 MyCanvas:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WPaintedWidget.h>
2 #include <Wt/WPainter.h>
3 #include <Wt/WPen.h>
4 #include <Wt/WBrush.h>
5 #include <Wt/WColor.h>
6
7 class MyCanvas : public Wt::WPaintedWidget
8 {
9 public:
10 MyCanvas() : Wt::WPaintedWidget()
11 {
12 resize({200, 200}); // 设置画布大小
13 }
14
15 protected:
16 void paintEvent(Wt::WPaintEvent event) override
17 {
18 Wt::WPainter painter(this);
19
20 // 设置画笔和画刷
21 painter.setPen(Wt::WPen(Wt::WColor::blue, 2.0)); // 蓝色画笔,线宽 2.0
22 painter.setBrush(Wt::WBrush(Wt::WColor::yellow)); // 黄色画刷
23
24 // 绘制矩形
25 painter.drawRect(10, 10, 180, 180);
26
27 // 绘制圆形
28 painter.setBrush(Wt::WBrush(Wt::WColor::red)); // 红色画刷
29 painter.drawEllipse({100, 100}, 50, 50); // 圆心 (100, 100), 半径 50
30 }
31 };

在应用中使用自定义绘图组件:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 MyCanvas *canvas = new MyCanvas();
2 root()->addWidget(canvas);

这段代码创建了一个自定义绘图组件 MyCanvas,并在 paintEvent() 函数中使用 WPainter 绘制了一个蓝色边框黄色填充的矩形和一个红色填充的圆形。

4.5 拖放操作 (Drag and Drop)

拖放 (Drag and Drop) 是一种直观的用户交互方式,允许用户通过拖拽组件来完成操作,例如移动组件、复制数据等。Wt 框架提供了拖放操作的支持。

4.5.1 拖放操作的实现 (Implementing Drag and Drop)

要实现拖放操作,需要:

设置组件为可拖拽 (Set Widget as Draggable): 使用 Wt::WWidget::setDraggable(bool draggable) 方法将组件设置为可拖拽。

设置拖拽数据 (Set Drag Data): 在拖拽开始时,使用 Wt::WWidget::setDragData(const Wt::WString& mimeType, const QByteArray& data) 方法设置拖拽数据。mimeType 指定数据类型 (例如 "text/plain", "application/x-wt-dnd-widget"),data 是实际的数据。

设置组件为拖放目标 (Set Widget as Drop Target): 使用 Wt::WWidget::setDropTarget(bool dropTarget) 方法将组件设置为拖放目标。

处理拖放事件 (Handle Drag and Drop Events): 响应拖放事件,例如 dragEnterEvent(), dropEvent() 等。在 dropEvent() 事件处理函数中,获取拖拽数据,并执行相应的操作。

4.5.2 拖放示例 (Drag and Drop Example)

源组件 (Source Widget) - 可拖拽的文本组件:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 Wt::WText *sourceText = new Wt::WText("Drag me!");
2 sourceText->setDraggable(true); // 设置为可拖拽
3 sourceText->setDragData("text/plain", "This is dragged text."); // 设置拖拽数据 (文本类型)
4 root()->addWidget(sourceText);

目标组件 (Target Widget) - 接收拖放的文本组件:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 class DropTarget : public Wt::WContainerWidget
2 {
3 public:
4 DropTarget() : Wt::WContainerWidget()
5 {
6 setStyleClass("drop-target"); // 设置 CSS 样式
7 setDropTarget(true); // 设置为拖放目标
8 setText("Drop text here");
9 }
10
11 protected:
12 void dropEvent(Wt::WDropEvent event) override
13 {
14 if (event.mimeType() == "text/plain") {
15 setText(event.mimeData().text()); // 获取拖拽文本并显示
16 }
17 }
18
19 void dragEnterEvent(Wt::WDragEnterEvent event) override
20 {
21 if (event.mimeType() == "text/plain") {
22 event.accept(); // 接受拖拽数据
23 }
24 }
25
26 private:
27 Wt::WText *textWidget = new Wt::WText();
28
29 void setText(const Wt::WString& text) {
30 clear();
31 addWidget(textWidget);
32 textWidget->setText(text);
33 }
34 };

CSS 样式 (CSS Style):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 .drop-target {
2 border: 2px dashed gray;
3 padding: 20px;
4 text-align: center;
5 }

在应用中使用拖放组件:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 DropTarget *target = new DropTarget();
2 root()->addWidget(target);

这段代码创建了一个可拖拽的文本组件 sourceText 和一个拖放目标组件 DropTarget。将 sourceText 拖拽到 DropTarget 区域内,DropTarget 会显示拖拽的文本内容。

通过学习本章内容,你已经掌握了 Wt 框架的高级组件和特性,可以构建更复杂、更交互性强的 Web 应用。在接下来的章节中,我们将继续深入学习 Wt 的数据处理、客户端-服务端通信、样式主题、国际化本地化、部署优化等方面的内容。 😊

5. chapter 5:Wt 数据处理与模型 (Data Handling and Models)

数据处理是任何 Web 应用的核心组成部分。Wt 框架提供了强大的数据处理 (Data Handling)模型 (Models) 机制,帮助开发者高效地管理和展示数据。本章将深入探讨 Wt 的数据架构,包括 MVC 架构、数据模型、数据绑定、数据验证以及数据库集成。

5.1 模型-视图-控制器 (MVC) 架构

模型-视图-控制器 (Model-View-Controller, MVC) 是一种广泛应用于软件工程中的架构模式,尤其在用户界面开发领域。MVC 模式旨在分离应用程序的三个主要方面,以提高代码的可维护性、可扩展性和可测试性。

模型 (Model)
模型负责管理应用程序的数据和业务逻辑。它独立于用户界面,并提供数据访问和操作的接口。模型可以包括数据结构、业务规则、数据验证和数据持久化等功能。

视图 (View)
视图负责展示用户界面,并将模型中的数据呈现给用户。视图是被动组件,它从模型中获取数据并进行显示,但不直接处理用户输入或业务逻辑。在 Wt 中,Widget 组件 主要承担视图的角色。

控制器 (Controller)
控制器充当模型和视图之间的 посредник (mediator)。它接收用户的输入(通常来自视图),并根据输入更新模型。当模型发生变化时,控制器会通知视图更新显示。在 Wt 中,事件处理机制 (信号与槽)WApplication 起到控制器的作用。

Wt 中的 MVC 模式 (MVC in Wt):

虽然 Wt 框架并没有严格地强制开发者遵循传统的 MVC 模式,但其设计理念和组件结构天然地支持 MVC 架构思想。

  • 模型 (Model): Wt 提供了 数据模型 (Data Model) 组件,例如 Wt::WAbstractItemModelWt::WStandardItemModel,用于管理和组织数据。开发者也可以自定义数据模型来适应特定的数据结构和业务需求。
  • 视图 (View): Wt 的 Widget 组件 充当视图的角色,负责展示数据模型中的数据。例如,Wt::WTableWt::WTree 组件可以与数据模型绑定,动态地显示模型中的数据。
  • 控制器 (Controller)Wt::WApplication信号与槽机制 共同承担控制器的角色。WApplication 管理应用程序的整体流程和状态,而信号与槽机制则处理用户交互事件,并根据事件更新数据模型和视图。当用户在界面上进行操作(例如点击按钮、编辑表格)时,Widget 组件会发出信号,连接到相应的槽函数(通常在 WApplication 或其他组件中定义)。槽函数作为控制器逻辑,负责更新数据模型,并通知 Widget 组件(视图)更新显示。

MVC 模式的优势 (Advantages of MVC in Wt):

代码分离 (Separation of Concerns): MVC 模式将应用程序划分为模型、视图和控制器三个部分,使得代码结构更加清晰,易于理解和维护。

提高可维护性 (Improved Maintainability): 由于模型、视图和控制器相互独立,修改其中一部分代码通常不会影响其他部分,降低了维护成本。

增强可测试性 (Enhanced Testability): 模型和控制器可以独立于视图进行单元测试,提高了代码的测试覆盖率和质量。

促进代码重用 (Promotes Code Reusability): 模型和控制器可以被多个视图复用,提高了代码的重用性。

并行开发 (Parallel Development): 不同的开发人员可以同时负责模型、视图和控制器的开发,提高了开发效率。

5.2 数据模型 (Data Models)

数据模型 (Data Model) 是 MVC 架构中的核心组件,负责管理应用程序的数据。在 Wt 框架中,数据模型通常用于为表格 (Wt::WTable)、树形控件 (Wt::WTree)、列表 (Wt::WListView) 等组件提供数据源。

5.2.1 Wt 数据模型类 (Wt Data Model Classes)

Wt 框架提供了多个抽象和具体的 Data Model 类:

Wt::WAbstractItemModel (Abstract Item Model)
这是一个抽象基类,定义了 Data Model 的基本接口。所有 Wt Data Model 类都继承自 WAbstractItemModel。它定义了访问模型数据的通用方法,例如获取数据、行数、列数、角色 (Roles) 等。

Wt::WAbstractListModel (Abstract List Model)
继承自 WAbstractItemModel,用于表示列表数据模型。

Wt::WAbstractTableModel (Abstract Table Model)
继承自 WAbstractItemModel,用于表示表格数据模型。

Wt::WAbstractTreeModel (Abstract Tree Model)
继承自 WAbstractItemModel,用于表示树形数据模型。

Wt::WStandardItemModel (Standard Item Model)
这是一个通用的、基于项 (Item) 的数据模型,可以用于表示列表、表格和树形数据。WStandardItemModel 内部使用树形结构存储数据,每个数据项 ( Wt::WStandardItem ) 可以包含多个属性,例如文本、图标、复选框状态等。

Wt::WSortFilterProxyModel (Sort Filter Proxy Model)
这是一个代理模型,可以对另一个数据模型进行排序和过滤操作,而无需修改原始模型的数据。

Wt::WSqlQueryModel (SQL Query Model)
这是一个特殊的模型,可以直接执行 SQL 查询并从数据库中获取数据。需要与数据库连接器配合使用,例如 Wt::Dbo::SqlConnectionPool

5.2.2 创建自定义数据模型 (Creating Custom Data Models)

如果 Wt 提供的内置数据模型不能满足需求,开发者可以创建自定义的数据模型。创建自定义数据模型通常需要继承 Wt::WAbstractItemModel 或其子类,并实现以下关键方法:

columnCount(const WModelIndex& parent = WModelIndex()) const override:
返回模型的列数。对于表格模型,返回列数;对于列表模型,通常返回 1。

rowCount(const WModelIndex& parent = WModelIndex()) const override:
返回模型的行数。对于表格模型,返回行数;对于列表模型,返回列表项的数量;对于树形模型,返回 parent 索引下的子节点数量。

data(const WModelIndex& index, ItemDataRole role = DisplayRole) const override:
返回指定索引 index 的数据。role 参数指定要获取的数据角色,例如 DisplayRole (用于显示)、EditRole (用于编辑) 等。

headerData(int section, Orientation orientation, ItemDataRole role = DisplayRole) const override:
返回表头数据。section 参数指定列索引或行索引,orientation 参数指定是行表头还是列表头,role 参数指定数据角色。

flags(const WModelIndex& index) const override:
返回指定索引 index 的标志 (Flags),用于指示该项是否可编辑、可选择等属性。

setData(const WModelIndex& index, const WVariant& value, ItemDataRole role = EditRole) override:
设置指定索引 index 的数据。如果模型需要支持数据编辑,则需要实现此方法。

示例代码:自定义列表数据模型 (Example Code: Custom List Data Model):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WAbstractListModel.h>
2 #include <Wt/WString.h>
3 #include <vector>
4
5 class MyStringListModel : public Wt::WAbstractListModel
6 {
7 public:
8 MyStringListModel(const std::vector<Wt::WString>& data) : data_(data) {}
9
10 int rowCount(const Wt::WModelIndex& parent = Wt::WModelIndex()) const override
11 {
12 return data_.size();
13 }
14
15 Wt::WVariant data(const Wt::WModelIndex& index, Wt::ItemDataRole role = Wt::DisplayRole) const override
16 {
17 if (role == Wt::DisplayRole && index.row() >= 0 && index.row() < data_.size()) {
18 return data_[index.row()];
19 }
20 return Wt::WVariant(); // 返回空 WVariant 表示没有数据
21 }
22
23 private:
24 std::vector<Wt::WString> data_;
25 };

这个例子创建了一个简单的列表数据模型 MyStringListModel,它基于 std::vector<Wt::WString> 存储字符串数据。

5.2.3 模型角色 (Model Roles) 与数据检索 (Data Retrieval)

模型角色 (Model Roles) 用于区分数据项的不同属性或用途。Wt::ItemDataRole 枚举定义了常用的数据角色,例如:

  • DisplayRole: 用于显示的文本数据。
  • EditRole: 用于编辑的文本数据。
  • DecorationRole: 用于显示的图标或装饰。
  • ToolTipRole: 工具提示文本。
  • UserRole: 自定义角色,开发者可以根据需要定义自己的角色。

data() 方法中,需要根据 role 参数返回不同角色的数据。例如,如果 roleDisplayRole,则返回用于显示的文本;如果 roleToolTipRole,则返回工具提示文本。

数据检索 (Data Retrieval) 通过 data() 方法实现。data() 方法接收一个 Wt::WModelIndex 对象作为参数,WModelIndex 用于唯一标识模型中的一个数据项。WModelIndex 包含行号、列号(对于表格模型)和父索引(对于树形模型)等信息。通过 WModelIndexdata() 方法可以定位到需要返回的数据项,并根据 role 参数返回相应的数据。

5.3 数据绑定 (Data Binding)

数据绑定 (Data Binding) 是一种将 UI 组件 (视图) 与数据模型连接起来的技术。当数据模型中的数据发生变化时,UI 组件会自动更新显示;反之,当用户在 UI 组件中修改数据时,数据模型也会自动更新。数据绑定可以简化 UI 开发,提高开发效率。

5.3.1 Wt 数据绑定机制 (Wt Data Binding Mechanisms)

Wt 框架提供了多种数据绑定机制:

模型-视图绑定 (Model-View Binding)
将 Widget 组件(例如 Wt::WTable, Wt::WTree, Wt::WListView, Wt::WComboBox 等)直接绑定到数据模型。当模型数据发生变化时,绑定的 Widget 组件会自动更新显示。通过 setModel() 方法将 Widget 组件与 Data Model 关联起来。

属性绑定 (Property Binding)
将 Widget 组件的属性(例如 Wt::WLineEdit::text(), Wt::WCheckBox::isChecked() 等)与数据模型的某个数据项或属性进行绑定。当数据模型的数据发生变化时,Widget 组件的属性会自动更新;反之,当 Widget 组件的属性发生变化时,数据模型的数据也会自动更新。Wt 使用 WBind 类来实现属性绑定。

信号-槽绑定 (Signal-Slot Binding) 与数据同步
虽然信号-槽机制本身不是专门为数据绑定设计的,但它可以与数据模型结合使用,实现数据的双向同步。例如,可以将 Widget 组件的 valueChanged() 信号连接到一个槽函数,槽函数负责更新数据模型中的数据;同时,当数据模型的数据发生变化时,可以通过信号通知 Widget 组件更新显示。

5.3.2 将 Widget 组件绑定到数据模型 (Binding Widgets to Data Models)

示例代码:将 WTable 绑定到 WStandardItemModel (Example Code: Binding WTable to WStandardItemModel):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WTable.h>
3 #include <Wt/WStandardItemModel.h>
4 #include <Wt/WText.h>
5 #include <Wt/WVBoxLayout.h>
6
7 class DataBindingExample : public Wt::WApplication
8 {
9 public:
10 DataBindingExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
11 {
12 setTitle("Data Binding Example");
13
14 Wt::WVBoxLayout *vbox = new Wt::WVBoxLayout();
15 Wt::WTable *table = new Wt::WTable();
16 vbox->addWidget(table);
17 root()->setLayout(vbox);
18
19 // 创建 WStandardItemModel
20 Wt::WStandardItemModel *model = new Wt::WStandardItemModel(3, 2); // 3 行 2 列
21 model->setHeaderData(0, Wt::Horizontal, "Name"); // 设置表头
22 model->setHeaderData(1, Wt::Horizontal, "Age");
23
24 // 添加数据
25 model->setItem(0, 0, std::make_unique<Wt::WStandardItem>("Alice"));
26 model->setItem(0, 1, std::make_unique<Wt::WStandardItem>("25"));
27 model->setItem(1, 0, std::make_unique<Wt::WStandardItem>("Bob"));
28 model->setItem(1, 1, std::make_unique<Wt::WStandardItem>("30"));
29 model->setItem(2, 0, std::make_unique<Wt::WStandardItem>("Charlie"));
30 model->setItem(2, 1, std::make_unique<Wt::WStandardItem>("28"));
31
32 // 将 WTable 绑定到 WStandardItemModel
33 table->setModel(model);
34 }
35 };

这段代码创建了一个 WStandardItemModel 并填充了数据,然后将 WTable 组件绑定到该模型。WTable 会自动显示模型中的数据。

5.3.3 属性绑定示例 (Property Binding Example)

示例代码:将 WLineEdittext 属性绑定到 WTexttext 属性 (Example Code: Binding WLineEdit::text to WText::text):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WLineEdit.h>
3 #include <Wt/WText.h>
4 #include <Wt/WVBoxLayout.h>
5 #include <Wt/WBind.h>
6
7 class PropertyBindingExample : public Wt::WApplication
8 {
9 public:
10 PropertyBindingExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
11 {
12 setTitle("Property Binding Example");
13
14 Wt::WVBoxLayout *vbox = new Wt::WVBoxLayout();
15 Wt::WLineEdit *lineEdit = new Wt::WLineEdit();
16 Wt::WText *textDisplay = new Wt::WText();
17 vbox->addWidget(lineEdit);
18 vbox->addWidget(textDisplay);
19 root()->setLayout(vbox);
20
21 // 将 lineEdit 的 text 属性绑定到 textDisplay 的 text 属性
22 Wt::WBind(lineEdit->textValue()).to(textDisplay->textValue());
23 }
24 };

在这个例子中,WBind(lineEdit->textValue()).to(textDisplay->textValue()) 实现了属性绑定。当用户在 lineEdit 中输入文本时,textDisplay 组件的文本内容会实时更新。

5.4 数据验证 (Data Validation)

数据验证 (Data Validation) 是确保用户输入的数据符合预定义的规则和约束的过程。数据验证可以防止无效数据进入系统,提高数据质量和应用程序的健壮性。

5.4.1 Wt 验证框架 (Wt Validation Framework)

Wt 框架提供了灵活的验证框架 (Validation Framework),可以与表单组件集成,实现客户端和服务器端的数据验证。

Wt 的验证框架主要包含以下组件:

Wt::WValidator (Validator)
这是一个抽象基类,定义了验证器的接口。Wt 提供了多种内置的验证器,例如 Wt::WIntValidator (整数验证器)、Wt::WDoubleValidator (浮点数验证器)、Wt::WRegExpValidator (正则表达式验证器) 等。开发者也可以自定义验证器。

Wt::WAbstractEdit (Abstract Edit)
这是一个抽象基类,表示可编辑的 Widget 组件,例如 Wt::WLineEdit, Wt::WSpinBox, Wt::WComboBox 等。WAbstractEdit 提供了 setValidator() 方法,用于设置验证器。

验证结果 (Validation Result)
验证器执行验证后,会返回一个 Wt::ValidationState 枚举值,表示验证结果:
▮▮▮▮ⓐ Valid: 数据有效。
▮▮▮▮ⓑ Invalid: 数据无效。
▮▮▮▮ⓒ Intermediate: 数据正在输入中,验证状态尚不确定。

5.4.2 使用验证器 (Using Validators)

示例代码:使用 WIntValidator 验证整数输入 (Example Code: Using WIntValidator to Validate Integer Input):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WLineEdit.h>
3 #include <Wt/WIntValidator.h>
4 #include <Wt/WText.h>
5 #include <Wt/WVBoxLayout.h>
6
7 class DataValidationExample : public Wt::WApplication
8 {
9 public:
10 DataValidationExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
11 {
12 setTitle("Data Validation Example");
13
14 Wt::WVBoxLayout *vbox = new Wt::WVBoxLayout();
15 Wt::WLineEdit *intLineEdit = new Wt::WLineEdit();
16 Wt::WText *validationResultText = new Wt::WText();
17 vbox->addWidget(intLineEdit);
18 vbox->addWidget(validationResultText);
19 root()->setLayout(vbox);
20
21 // 创建 WIntValidator,限制输入为 0-100 的整数
22 Wt::WIntValidator *intValidator = new Wt::WIntValidator(0, 100);
23 intLineEdit->setValidator(intValidator); // 设置验证器
24
25 intLineEdit->keyWentUp().connect([=]() {
26 Wt::ValidationState state = intValidator->validate(intLineEdit->text());
27 if (state == Wt::ValidationState::Valid) {
28 validationResultText->setText("Input is valid integer.");
29 validationResultText->setStyleClass("valid-text"); // 设置有效样式
30 } else {
31 validationResultText->setText("Input is invalid integer!");
32 validationResultText->setStyleClass("invalid-text"); // 设置无效样式
33 }
34 });
35 }
36 };

CSS 样式 (CSS Style):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 .valid-text {
2 color: green;
3 }
4
5 .invalid-text {
6 color: red;
7 }

这段代码创建了一个 WLineEdit 组件,并为其设置了 WIntValidator 验证器。当用户输入文本时,验证器会实时验证输入是否为 0-100 的整数,并将验证结果显示在 validationResultText 组件中。

5.4.3 自定义验证器 (Custom Validators)

开发者可以继承 Wt::WValidator 类,并重写 validate() 方法来创建自定义的验证器,以满足特定的验证需求。

示例代码:自定义邮箱格式验证器 (Example Code: Custom Email Format Validator):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WValidator.h>
2 #include <Wt/WString.h>
3 #include <QRegularExpression>
4
5 class EmailValidator : public Wt::WValidator
6 {
7 public:
8 EmailValidator() : Wt::WValidator() {}
9
10 Wt::ValidationState validate(Wt::WString& input) override
11 {
12 QRegularExpression emailRegex(R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})"); // 邮箱正则表达式
13 if (emailRegex.match(input).hasMatch() || input.isEmpty()) { // 允许为空
14 return Wt::ValidationState::Valid;
15 } else {
16 return Wt::ValidationState::Invalid;
17 }
18 }
19 };

然后,可以将 EmailValidator 应用到 WLineEdit 组件,验证邮箱格式。

5.4.4 显示验证错误 (Displaying Validation Errors)

Wt 框架会自动在验证失败的表单组件旁边显示错误提示信息。可以通过以下方式定制错误提示:

  • WAbstractEdit::setToolTip(): 设置工具提示,当验证错误时,工具提示会显示错误信息。
  • WAbstractEdit::setInvalidStyleClass(): 设置验证无效时的 CSS 类名,可以定制错误提示的样式。
  • WAbstractEdit::setWarningStyleClass(): 设置验证警告时的 CSS 类名。

5.5 数据库集成 (Database Integration)

在实际的 Web 应用中,数据通常存储在数据库中。Wt 框架可以与多种数据库进行集成,方便开发者从数据库中读取数据、将数据存储到数据库以及执行数据库操作。

5.5.1 数据库连接 (Database Connection)

Wt 框架本身不提供数据库连接的具体实现,而是依赖于第三方的 C++ 数据库库。常用的 C++ 数据库库包括:

  • SOCI (C++ Database Access Library): 一个流行的开源 C++ 数据库库,支持多种数据库,例如 PostgreSQL, MySQL, SQLite, Oracle, SQL Server 等。Wt 官方示例和文档中经常使用 SOCI 库进行数据库集成。
  • Qt SQL: Qt 框架提供的数据库模块,如果你的项目已经使用了 Qt 框架,Qt SQL 是一个不错的选择。Qt SQL 支持多种数据库,并且与 Qt 框架集成良好。
  • ODBC (Open Database Connectivity): ODBC 是一种通用的数据库接口标准,可以通过 ODBC 驱动程序连接到各种数据库。
  • 其他 C++ 数据库库: 例如 libpqxx (PostgreSQL C++ 客户端库), mysql-connector-cpp (MySQL Connector/C++), sqlitecpp (SQLite C++ 封装库) 等。

要进行数据库集成,首先需要选择合适的 C++ 数据库库,并将其集成到 Wt 项目中。然后,需要建立数据库连接。

示例代码:使用 SOCI 连接 PostgreSQL 数据库 (Example Code: Using SOCI to Connect to PostgreSQL Database):

首先需要安装 SOCI 库和 PostgreSQL SOCI backend。

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WText.h>
3 #include <Wt/WVBoxLayout.h>
4 #include <soci/soci.h>
5 #include <soci/postgresql/soci-postgresql.h> // PostgreSQL backend
6
7 class DatabaseExample : public Wt::WApplication
8 {
9 public:
10 DatabaseExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
11 {
12 setTitle("Database Example");
13
14 Wt::WVBoxLayout *vbox = new Wt::WVBoxLayout();
15 Wt::WText *dbResultText = new Wt::WText();
16 vbox->addWidget(dbResultText);
17 root()->setLayout(vbox);
18
19 try {
20 soci::session sql;
21 sql.open(soci::postgresql, "dbname=mydb user=myuser password=mypassword host=localhost port=5432"); // PostgreSQL 连接字符串
22
23 dbResultText->setText("Database connection successful!");
24 } catch (const soci::soci_error& e) {
25 dbResultText->setText("Database connection failed: " + Wt::WString::fromUTF8(e.what()));
26 }
27 }
28 };

5.5.2 数据库操作 (Database Operations)

建立数据库连接后,可以使用选定的数据库库提供的 API 执行数据库操作,例如查询数据、插入数据、更新数据、删除数据等。

示例代码:使用 SOCI 查询数据库数据并显示在 WTable 中 (Example Code: Using SOCI to Query Database Data and Display in WTable):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WTable.h>
3 #include <Wt/WText.h>
4 #include <Wt/WVBoxLayout.h>
5 #include <Wt/WStandardItemModel.h>
6 #include <soci/soci.h>
7 #include <soci/postgresql/soci-postgresql.h>
8
9 class DatabaseTableExample : public Wt::WApplication
10 {
11 public:
12 DatabaseTableExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
13 {
14 setTitle("Database Table Example");
15
16 Wt::WVBoxLayout *vbox = new Wt::WVBoxLayout();
17 Wt::WTable *dataTable = new Wt::WTable();
18 vbox->addWidget(dataTable);
19 root()->setLayout(vbox);
20
21 try {
22 soci::session sql;
23 sql.open(soci::postgresql, "dbname=mydb user=myuser password=mypassword host=localhost port=5432");
24
25 Wt::WStandardItemModel *model = new Wt::WStandardItemModel(0, 2); // 初始化空模型,行数未知
26 model->setHeaderData(0, Wt::Horizontal, "ID");
27 model->setHeaderData(1, Wt::Horizontal, "Name");
28 dataTable->setModel(model);
29
30 soci::rowset<soci::row> rs = (sql.prepare << "SELECT id, name FROM my_table"); // SQL 查询语句
31 int row = 0;
32 for (const auto& r : rs) {
33 model->insertRow(row);
34 model->setItem(row, 0, std::make_unique<Wt::WStandardItem>(Wt::WString::fromUTF8(r.get<int>(0)))); // ID 列
35 model->setItem(row, 1, std::make_unique<Wt::WStandardItem>(Wt::WString::fromUTF8(r.get<std::string>(1)))); // Name 列
36 row++;
37 }
38
39 } catch (const soci::soci_error& e) {
40 dataTable->elementAt(0, 0)->addWidget(std::make_unique<Wt::WText>("Database error: " + Wt::WString::fromUTF8(e.what())));
41 }
42 }
43 };

这段代码使用 SOCI 库连接 PostgreSQL 数据库,执行 SQL 查询语句 SELECT id, name FROM my_table,并将查询结果显示在 WTable 组件中。

通过本章的学习,你已经掌握了 Wt 框架的数据处理和模型相关知识,包括 MVC 架构、数据模型、数据绑定、数据验证和数据库集成。这些知识将帮助你构建数据驱动的、功能强大的 Web 应用。 😊 在接下来的章节中,我们将继续深入学习 Wt 的客户端-服务端通信、样式主题、国际化本地化、部署优化等方面的内容。

6. chapter 6:Wt 客户端-服务端通信 (Client-Server Communication)

Wt 框架的核心特性之一是其高效且强大的客户端-服务端通信 (Client-Server Communication) 机制。由于 Wt 应用采用服务端渲染架构,客户端与服务器之间的频繁交互是实现富客户端用户体验的关键。本章将深入探讨 Wt 提供的各种客户端-服务端通信技术,包括 AJAX 与异步请求、WebSocket 支持、服务器推送以及文件上传与下载。

6.1 AJAX 与异步请求 (AJAX and Asynchronous Requests)

AJAX (Asynchronous JavaScript and XML) 是一种在无需重新加载整个网页的情况下,与服务器交换数据并更新部分网页的技术。虽然 AJAX 的名字中包含 "XML",但现在更常用 JSON (JavaScript Object Notation) 作为数据交换格式。在 Wt 框架中,AJAX 是实现客户端与服务器异步通信的基础技术。

6.1.1 Wt 中的异步请求 (Asynchronous Requests in Wt)

Wt 框架自动处理了大部分底层的 AJAX 通信细节。开发者通常不需要直接编写 JavaScript 代码来发起 AJAX 请求。Wt 通过 信号 (Signals)槽 (Slots) 机制,以及 WServerWApplication 框架,实现了透明的异步通信。

当用户在客户端进行操作(例如点击按钮、输入文本)时,Wt 客户端 JavaScript 代码会将事件信息序列化并通过 AJAX 请求发送到服务器端。服务器端的 WApplication 实例接收并处理这些请求,然后将 UI 更新指令(通常也是 JSON 格式)异步地发送回客户端。客户端接收到指令后,会高效地更新页面 DOM,实现界面的动态变化。

关键特点 (Key Features):

异步性 (Asynchronous): 客户端发起请求后,无需等待服务器响应,可以继续执行其他操作,不会阻塞用户界面。

局部更新 (Partial Update): 只更新页面中需要变化的部分,而不是重新加载整个页面,提高了效率和用户体验。

服务端驱动 (Server-Driven): 所有的业务逻辑和 UI 状态都由服务器端控制,客户端只负责展示和用户交互。

6.1.2 手动发起异步请求 (Manually Initiating Asynchronous Requests)

在某些情况下,开发者可能需要手动发起异步请求,例如:

  • 定期轮询服务器 (Polling): 客户端定期向服务器发送请求,检查是否有新的数据或更新。通常不推荐,因为效率较低。
  • 长轮询 (Long Polling): 客户端向服务器发送请求后,服务器保持连接打开,直到有新的数据或超时才返回响应。比轮询效率更高,但仍然不如 WebSocket。
  • 按需加载数据 (Lazy Loading): 当用户滚动页面或进行特定操作时,异步加载更多数据。

Wt 提供了 Wt::Http::Client 类,用于手动发起 HTTP 请求。

示例代码:使用 Wt::Http::Client 发起 GET 请求 (Example Code: Using Wt::Http::Client to Send a GET Request):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WPushButton.h>
3 #include <Wt/WText.h>
4 #include <Wt/WVBoxLayout.h>
5 #include <Wt/Http/Client.h>
6 #include <Wt/Json/Object.h>
7 #include <Wt/Json/Parser.h>
8
9 class AjaxExample : public Wt::WApplication
10 {
11 public:
12 AjaxExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
13 {
14 setTitle("AJAX Example");
15
16 Wt::WVBoxLayout *vbox = new Wt::WVBoxLayout();
17 Wt::WPushButton *requestButton = new Wt::WPushButton("Send Request");
18 Wt::WText *responseText = new Wt::WText("Response will be shown here.");
19 vbox->addWidget(requestButton);
20 vbox->addWidget(responseText);
21 root()->setLayout(vbox);
22
23 requestButton->clicked().connect([=]() {
24 Wt::Http::Client *httpClient = new Wt::Http::Client(this); // 创建 HTTP 客户端
25 httpClient->get("https://api.example.com/data"); // 发起 GET 请求
26
27 httpClient->done().connect([=](Wt::Http::Message response) { // 请求成功回调
28 if (response.status() == 200) {
29 Wt::Json::Parser parser;
30 Wt::Json::Value jsonResponse = parser.parse(response.body()); // 解析 JSON 响应
31
32 if (jsonResponse.isObject()) {
33 Wt::Json::Object responseObject = jsonResponse.asObject();
34 Wt::WString data = responseObject.get("message").toString(); // 获取 "message" 字段
35
36 responseText->setText("Response: " + data);
37 } else {
38 responseText->setText("Invalid JSON response.");
39 }
40 } else {
41 responseText->setText("Request failed: " + Wt::WString::fromUTF8(response.statusLine()));
42 }
43 delete httpClient; // 释放 HTTP 客户端资源
44 });
45
46 httpClient->failed().connect([=](boost::system::error_code error) { // 请求失败回调
47 responseText->setText("Request failed: " + Wt::WString::fromUTF8(error.message()));
48 delete httpClient;
49 });
50 });
51 }
52 };

这段代码演示了如何使用 Wt::Http::Client 发起一个 GET 请求到 https://api.example.com/data,并处理服务器返回的 JSON 响应。

6.2 WebSocket 支持

WebSocket 是一种在单个 TCP 连接上进行全双工通信的网络协议。与传统的 HTTP 请求-响应模式不同,WebSocket 允许服务器主动向客户端推送数据,实现实时 (Real-time)双向 (Bi-directional) 通信。

6.2.1 Wt 中的 WebSocket 支持 (WebSocket Support in Wt)

Wt 框架对 WebSocket 提供了良好的支持。使用 WebSocket 可以构建需要实时数据更新的应用,例如在线聊天、实时监控、多人协作等。

Wt WebSocket 相关类 (Wt WebSocket Related Classes):

Wt::WebSocket: 客户端 WebSocket 类,用于在客户端 JavaScript 代码中创建和管理 WebSocket 连接。

Wt::WebSocketServer: 服务器端 WebSocket 类,用于处理 WebSocket 连接请求和管理 WebSocket 连接。

Wt::WSession: Wt 会话类,每个 WebSocket 连接都与一个 Wt 会话关联。

6.2.2 创建 WebSocket 服务端 (Creating WebSocket Server)

要在 Wt 应用中启用 WebSocket 服务端,需要在 WServer 中注册 WebSocketServer

示例代码:创建 WebSocket 服务端 (Example Code: Creating WebSocket Server):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WServer.h>
3 #include <Wt/WebSocketServer.h>
4 #include <Wt/WText.h>
5 #include <Wt/WVBoxLayout.h>
6
7 class WebSocketApp : public Wt::WApplication
8 {
9 public:
10 WebSocketApp(const Wt::WEnvironment& env) : Wt::WApplication(env), wsServer_(nullptr)
11 {
12 setTitle("WebSocket Example");
13
14 Wt::WVBoxLayout *vbox = new Wt::WVBoxLayout();
15 Wt::WText *statusText = new Wt::WText("WebSocket status: Disconnected");
16 vbox->addWidget(statusText);
17 root()->setLayout(vbox);
18
19 wsServer_ = new Wt::WebSocketServer(this); // 创建 WebSocketServer 实例
20
21 wsServer_->connected().connect([=](std::unique_ptr<Wt::WebSocket> socket) { // 新连接回调
22 statusText->setText("WebSocket status: Connected");
23 Wt::log("info") << "WebSocket connected from " << socket->peerAddress();
24
25 socket->messageReceived().connect([=](const std::string& message) { // 接收消息回调
26 Wt::log("info") << "WebSocket message received: " << message;
27 socket->sendMessage("Server received: " + message); // 发送消息回客户端
28 });
29
30 socket->disconnected().connect([=]() { // 断开连接回调
31 statusText->setText("WebSocket status: Disconnected");
32 Wt::log("info") << "WebSocket disconnected";
33 });
34 });
35 }
36
37 private:
38 Wt::WebSocketServer *wsServer_;
39 };
40
41 int main(int argc, char** argv)
42 {
43 Wt::WServer server(argc, argv);
44
45 server.addApplication(server.getContextPath(), [](const Wt::WEnvironment& env) {
46 return std::make_unique<WebSocketApp>(env);
47 });
48
49 if (server.start()) {
50 Wt::WServer::waitForShutdown();
51 server.stop();
52 }
53 return 0;
54 }

6.2.3 创建 WebSocket 客户端 (Creating WebSocket Client)

在客户端 JavaScript 代码中,可以使用 Wt.WebSocket 类创建 WebSocket 客户端,并连接到 WebSocket 服务端。

示例 JavaScript 代码:创建 WebSocket 客户端 (Example JavaScript Code: Creating WebSocket Client):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 // 获取 Wt 应用根元素
2 var rootElement = document.getElementById('root');
3
4 // 创建 WebSocket 对象,连接到 WebSocket 服务端 URL
5 var websocket = new Wt.WebSocket("ws://localhost:8080/websocket"); // 假设 WebSocket 服务端 URL 为 /websocket
6
7 websocket.onopen = function() {
8 console.log("WebSocket connection opened");
9 // 连接成功后发送消息
10 websocket.send("Hello from client!");
11 };
12
13 websocket.onmessage = function(event) {
14 console.log("WebSocket message received: " + event.data);
15 // 处理接收到的消息
16 };
17
18 websocket.onclose = function() {
19 console.log("WebSocket connection closed");
20 };
21
22 websocket.onerror = function(error) {
23 console.error("WebSocket error: " + error);
24 };

注意:需要在 WServer 中配置 WebSocket 服务端 URL 路径。

6.3 服务器推送 (Server Push)

服务器推送 (Server Push) 是一种服务器主动向客户端推送数据的技术,无需客户端显式请求。WebSocket 是实现服务器推送的一种常用方式。Wt 框架也支持基于 AJAX 的服务器推送技术,例如 Comet (Comet)Server-Sent Events (SSE)

6.3.1 Wt 服务器推送机制 (Wt Server Push Mechanisms)

Wt 框架提供了多种服务器推送机制,开发者可以根据应用需求选择合适的技术。

WebSocket (WebSocket): 如前所述,WebSocket 提供了全双工、实时的服务器推送能力。

Server-Sent Events (SSE): SSE 是一种单向的服务器推送技术,服务器可以持续向客户端推送文本数据流。SSE 基于 HTTP 协议,实现简单,浏览器兼容性较好。

Comet (Comet): Comet 是一种基于 AJAX 的服务器推送技术,通过长连接或流式传输等方式模拟服务器推送效果。Comet 技术较为复杂,效率相对较低,现在逐渐被 WebSocket 和 SSE 取代。

6.3.2 使用服务器推送更新 UI (Using Server Push to Update UI)

服务器推送通常用于实时更新 UI 组件,例如实时数据仪表盘、在线聊天消息、实时通知等。

示例代码:使用 WebSocket 服务器推送实时时间 (Example Code: Using WebSocket Server Push Real-time Time):

服务器端代码 (Server-side Code - in WebSocketApp class from 6.2.2):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WTimer.h>
2 #include <Wt/WDateTime.h>
3
4 // ... (WebSocketApp class definition from 6.2.2)
5
6 class WebSocketApp : public Wt::WApplication
7 {
8 // ... (Constructor and other members)
9
10 WebSocketApp(const Wt::WEnvironment& env) : Wt::WApplication(env), wsServer_(nullptr), timeText_(nullptr)
11 {
12 // ... (Initialization code from 6.2.2)
13
14 timeText_ = new Wt::WText();
15 vbox->addWidget(timeText_);
16
17 Wt::WTimer *timer = new Wt::WTimer(this); // 创建定时器
18 timer->setInterval(std::chrono::seconds(1)); // 设置定时器间隔为 1 秒
19 timer->timeout().connect([=]() { // 定时器超时回调
20 Wt::WDateTime now = Wt::WDateTime::currentDateTime();
21 Wt::WString timeString = now.toString("hh:mm:ss");
22 timeText_->setText("Current time: " + timeString);
23
24 if (wsServer_ && wsServer_->isConnected()) { // 如果 WebSocket 连接已建立
25 wsServer_->sendMessageToAll("Server time: " + timeString.toUTF8()); // 向所有客户端推送时间
26 }
27 });
28 timer->start(); // 启动定时器
29 }
30
31 private:
32 Wt::WebSocketServer *wsServer_;
33 Wt::WText *timeText_;
34 };

客户端 JavaScript 代码 (Client-side JavaScript Code - in websocket.onmessage function from 6.2.3):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 websocket.onmessage = function(event) {
2 console.log("WebSocket message received: " + event.data);
3 // 将服务器推送的时间显示在页面上
4 document.getElementById('server-time').innerText = event.data; // 假设页面上有一个 id 为 'server-time' 的元素用于显示服务器时间
5 };

需要在 HTML 页面中添加一个用于显示服务器时间的元素,例如 <div id="server-time"></div>

这段代码使用 Wt::WTimer 定时获取服务器当前时间,并通过 WebSocket 服务器推送给所有连接的客户端,客户端 JavaScript 代码接收到时间后更新页面显示。

6.4 文件上传与下载 (File Upload and Download)

文件上传 (File Upload)文件下载 (File Download) 是 Web 应用中常见的需求。Wt 框架提供了 Wt::WFileUpload 组件用于文件上传,以及 Wt::WLink 组件和 HTTP 响应处理机制用于文件下载。

6.4.1 文件上传 (File Upload)

Wt::WFileUpload 组件 提供了方便的文件上传功能。

Wt::WFileUpload 的使用 (Using Wt::WFileUpload):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WFileUpload.h>
3 #include <Wt/WPushButton.h>
4 #include <Wt/WText.h>
5 #include <Wt/WVBoxLayout.h>
6 #include <fstream>
7
8 class FileUploadExample : public Wt::WApplication
9 {
10 public:
11 FileUploadExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
12 {
13 setTitle("File Upload Example");
14
15 Wt::WVBoxLayout *vbox = new Wt::WVBoxLayout();
16 Wt::WFileUpload *fileUpload = new Wt::WFileUpload();
17 Wt::WPushButton *uploadButton = new Wt::WPushButton("Upload");
18 Wt::WText *uploadStatusText = new Wt::WText();
19 vbox->addWidget(fileUpload);
20 vbox->addWidget(uploadButton);
21 vbox->addWidget(uploadStatusText);
22 root()->setLayout(vbox);
23
24 fileUpload->setFilters("image/*, text/*"); // 设置允许上传的文件类型
25 fileUpload->setMaximumSize(10 * 1024 * 1024); // 设置最大文件大小 (10MB)
26
27 uploadButton->clicked().connect([=]() {
28 if (fileUpload->isReady()) { // 文件已选择
29 std::string fileName = fileUpload->clientFileName().toUTF8(); // 获取客户端文件名
30 std::string contentType = fileUpload->contentType().toUTF8(); // 获取 Content-Type
31 std::istream *fileStream = fileUpload->spoolFile(); // 获取文件输入流
32
33 std::ofstream outputFile("uploads/" + fileName, std::ios::binary); // 服务器端保存路径
34 outputFile << fileStream->rdbuf(); // 将文件流写入输出文件
35 outputFile.close();
36
37 uploadStatusText->setText("File uploaded successfully: " + fileName + " (" + contentType + ")");
38 } else {
39 uploadStatusText->setText("Please select a file to upload.");
40 }
41 });
42 }
43 };

这段代码创建了一个 WFileUpload 组件,允许用户选择文件上传。当点击 "Upload" 按钮时,服务器端会将上传的文件保存到 uploads/ 目录下。

6.4.2 文件下载 (File Download)

Wt::WLink 组件 可以用于创建文件下载链接。当用户点击链接时,浏览器会下载指定的文件。

示例代码:创建文件下载链接 (Example Code: Creating File Download Link):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WLink.h>
3 #include <Wt/WVBoxLayout.h>
4 #include <fstream>
5
6 class FileDownloadExample : public Wt::WApplication
7 {
8 public:
9 FileDownloadExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
10 {
11 setTitle("File Download Example");
12
13 Wt::WVBoxLayout *vbox = new Wt::WVBoxLayout();
14 Wt::WLink *downloadLink = new Wt::WLink(Wt::WLink::InternalPath, "/download/example.txt"); // 创建内部路径链接
15 downloadLink->setText("Download Example File");
16 vbox->addWidget(downloadLink);
17 root()->setLayout(vbox);
18
19 // 创建示例文件 (可选,如果文件已存在则跳过)
20 std::ofstream exampleFile("public/download/example.txt");
21 exampleFile << "This is an example file for download.";
22 exampleFile.close();
23 }
24
25 Wt::cpp17::any resolvePath(const std::string& path) override
26 {
27 if (path.rfind("/download/", 0) == 0) { // 匹配下载路径
28 std::string filePath = "public" + path; // 文件实际路径
29 return Wt::cpp17::any(Wt::WLink::Path(filePath)); // 返回文件路径链接
30 }
31 return Wt::WApplication::resolvePath(path); // 调用父类方法处理其他路径
32 }
33 };

这段代码创建了一个 WLink 组件,链接到内部路径 /download/example.txt。当用户点击链接时,resolvePath() 方法会将内部路径解析为实际的文件路径 public/download/example.txt,并返回文件下载链接。需要确保 public/download/example.txt 文件存在,或者在 resolvePath() 方法中动态生成文件内容并返回流。

通过本章的学习,你已经深入了解了 Wt 框架的客户端-服务端通信机制,包括 AJAX 异步请求、WebSocket 支持、服务器推送以及文件上传下载。这些技术是构建交互性强、功能丰富的 Web 应用的关键。 😊 在接下来的章节中,我们将继续学习 Wt 的样式与主题、国际化本地化、部署优化等方面的内容。

7. chapter 7:Wt 样式与主题 (Styling and Themes)

Web 应用的用户界面 (User Interface, UI) 不仅要功能完善,还要美观易用。样式 (Styling)主题 (Themes) 在提升用户体验方面起着至关重要的作用。Wt 框架提供了强大的样式和主题机制,允许开发者轻松地定制应用的外观,使其更具吸引力和专业性。本章将深入探讨 Wt 的样式和主题功能,包括 CSS 样式表集成、主题的使用与定制以及响应式设计。

7.1 CSS 样式表集成

CSS (Cascading Style Sheets) 是一种用于描述 HTML (或 XML) 文档呈现样式的样式表语言。在 Wt 应用中,虽然主要使用 C++ 代码构建界面,但 CSS 仍然是控制组件外观的重要手段。Wt 框架可以无缝集成 CSS 样式表,让开发者利用 CSS 的强大功能来定制组件的样式。

7.1.1 引入 CSS 样式表

在 Wt 应用中引入 CSS 样式表主要有两种方式:

全局样式表 (Global Stylesheet)
全局样式表应用于整个 Wt 应用。可以通过 Wt::WApplication::setStyleSheet() 方法设置全局样式表。通常在 WApplication 的构造函数中设置。

示例代码:设置全局样式表 (Example Code: Setting Global Stylesheet):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2
3 class StylingExample : public Wt::WApplication
4 {
5 public:
6 StylingExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
7 {
8 setTitle("Styling Example");
9
10 // 设置全局 CSS 样式表文件
11 setStyleSheet("url(style.css)"); // 假设 style.css 文件位于 public 目录下
12 }
13 };

需要将 CSS 样式表文件 (style.css) 放置在 Wt 应用的 public 目录下,或者配置正确的资源路径。

组件级别样式表 (Widget-Level Stylesheet)
组件级别样式表只应用于特定的 Widget 组件及其子组件。可以通过 Wt::WWidget::setStyleClass() 方法为组件设置 CSS 类名,然后在 CSS 样式表中定义该类名的样式。

示例代码:设置组件级别样式表 (Example Code: Setting Widget-Level Stylesheet):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WPushButton.h>
3 #include <Wt/WVBoxLayout.h>
4
5 class StylingExample : public Wt::WApplication
6 {
7 public:
8 StylingExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
9 {
10 setTitle("Styling Example");
11
12 Wt::WVBoxLayout *vbox = new Wt::WVBoxLayout();
13 Wt::WPushButton *styledButton = new Wt::WPushButton("Styled Button");
14 vbox->addWidget(styledButton);
15 root()->setLayout(vbox);
16
17 styledButton->setStyleClass("my-button"); // 设置 CSS 类名 "my-button"
18 }
19 };

CSS 样式表文件 (style.css):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 .my-button {
2 background-color: #4CAF50; /* 绿色背景 */
3 color: white; /* 白色文本 */
4 padding: 10px 20px; /* 内边距 */
5 border: none; /* 无边框 */
6 border-radius: 5px; /* 圆角 */
7 cursor: pointer; /* 鼠标悬停时显示手型光标 */
8 }
9
10 .my-button:hover {
11 background-color: #45a049; /* 鼠标悬停时颜色变深 */
12 }

7.1.2 CSS 选择器和 Wt 组件

在 CSS 样式表中,可以使用各种 CSS 选择器 (Selectors) 来选取需要应用样式的 Wt 组件。常用的 CSS 选择器包括:

类选择器 (Class Selector): 以 . 开头,选取具有指定 CSS 类名的组件。例如 .my-button 选取所有 CSS 类名为 "my-button" 的组件。

ID 选择器 (ID Selector): 以 # 开头,选取具有指定 ID 的组件。可以使用 Wt::WWidget::setId() 方法为组件设置 ID。例如 #my-text 选取 ID 为 "my-text" 的组件。通常不推荐过度使用 ID 选择器,类选择器更灵活和可维护。

标签选择器 (Tag Selector): 直接使用 HTML 标签名,选取指定类型的 HTML 元素。由于 Wt 组件最终会渲染成 HTML 元素,可以使用标签选择器来影响特定类型的组件。例如 button 选取所有 <button> 元素,input 选取所有 <input> 元素。需要了解 Wt 组件渲染成的 HTML 结构,才能有效使用标签选择器。

后代选择器 (Descendant Selector): 使用空格分隔,选取指定元素的后代元素。例如 .container button 选取 CSS 类名为 "container" 的组件内部的所有 <button> 元素。

子选择器 (Child Selector): 使用 > 分隔,选取指定元素的直接子元素。例如 .container > button 选取 CSS 类名为 "container" 的组件的直接子元素 <button>

属性选择器 (Attribute Selector): 根据 HTML 元素的属性选取元素。例如 input[type="text"] 选取所有 type 属性为 "text" 的 <input> 元素。

Wt 组件的 HTML 结构 (HTML Structure of Wt Widgets):

理解 Wt 组件渲染成的 HTML 结构对于编写 CSS 样式表非常重要。可以使用浏览器的开发者工具 (例如 Chrome DevTools, Firefox Developer Tools) 查看 Wt 应用的 HTML 结构,分析组件的类名、ID、标签名等信息,从而编写精确的 CSS 选择器。

例如,Wt::WPushButton 组件通常会渲染成一个 <button> 元素,并可能包含一些 Wt 框架自动生成的 CSS 类名。

7.1.3 动态样式修改

除了使用 CSS 样式表,还可以在 C++ 代码中动态地修改组件的样式。Wt 提供了以下方法进行动态样式修改:

Wt::WWidget::setStyleProperty(const std::string& name, const std::string& value): 设置组件的内联 CSS 样式属性。name 是 CSS 属性名 (例如 "background-color", "color", "font-size"),value 是属性值。

示例代码:动态修改按钮背景颜色 (Example Code: Dynamically Changing Button Background Color):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 Wt::WPushButton *dynamicButton = new Wt::WPushButton("Dynamic Style Button");
2 dynamicButton->setStyleProperty("background-color", "lightblue"); // 设置背景颜色为浅蓝色
3 dynamicButton->setStyleProperty("color", "black"); // 设置文本颜色为黑色

Wt::WWidget::addStyleClass(const std::string& styleClass)Wt::WWidget::removeStyleClass(const std::string& styleClass): 动态添加或移除组件的 CSS 类名。

示例代码:动态切换按钮样式 (Example Code: Dynamically Switching Button Style):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 Wt::WPushButton *toggleButton = new Wt::WPushButton("Toggle Style");
2 toggleButton->setStyleClass("style-normal"); // 初始样式为 "style-normal"
3
4 toggleButton->clicked().connect([=]() {
5 if (toggleButton->hasStyleClass("style-normal")) {
6 toggleButton->removeStyleClass("style-normal");
7 toggleButton->addStyleClass("style-highlight"); // 切换到 "style-highlight" 样式
8 } else {
9 toggleButton->removeStyleClass("style-highlight");
10 toggleButton->addStyleClass("style-normal"); // 切换回 "style-normal" 样式
11 }
12 });

CSS 样式表 (style.css):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 .style-normal {
2 background-color: #f0f0f0;
3 color: black;
4 border: 1px solid #ccc;
5 }
6
7 .style-highlight {
8 background-color: orange;
9 color: white;
10 border: none;
11 }

动态样式修改可以实现更丰富的交互效果和组件状态变化。但应谨慎使用,过度使用内联样式可能会降低 CSS 样式表的可维护性。

7.2 主题 (Themes) 的使用与定制

主题 (Theme) 是一组预定义的样式集合,用于统一应用程序的整体外观风格。Wt 框架提供了一些内置主题,同时也支持自定义主题。使用主题可以快速改变应用的整体风格,并保持风格的一致性。

7.2.1 Wt 内置主题

Wt 框架内置了一些主题,可以通过 Wt::WApplication::setTheme() 方法应用。常用的内置主题包括:

"standard": Wt 默认主题,简洁、现代的风格。

"bootstrap": 基于 Bootstrap 框架的主题,具有 Bootstrap 的经典风格和响应式布局特性。需要引入 Bootstrap CSS 和 JavaScript 文件。

"material": 基于 Material Design 风格的主题,具有 Google Material Design 的视觉效果。需要引入 Material Design CSS 和 JavaScript 文件。

"webtoolkit": Wt 旧版本的主题,风格较为传统。

"polished": 一个更加现代和精致的主题。

"translucent": 半透明风格的主题。

7.2.2 应用主题

示例代码:应用内置主题 (Example Code: Applying Built-in Themes):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WComboBox.h>
3 #include <Wt/WText.h>
4 #include <Wt/WVBoxLayout.h>
5
6 class ThemeExample : public Wt::WApplication
7 {
8 public:
9 ThemeExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
10 {
11 setTitle("Theme Example");
12
13 Wt::WVBoxLayout *vbox = new Wt::WVBoxLayout();
14 Wt::WComboBox *themeComboBox = new Wt::WComboBox();
15 Wt::WText *themeDescription = new Wt::WText();
16 vbox->addWidget(themeComboBox);
17 vbox->addWidget(themeDescription);
18 root()->setLayout(vbox);
19
20 // 添加主题选项
21 themeComboBox->addItem("standard");
22 themeComboBox->addItem("bootstrap");
23 themeComboBox->addItem("material");
24 themeComboBox->addItem("webtoolkit");
25 themeComboBox->addItem("polished");
26 themeComboBox->addItem("translucent");
27
28 themeComboBox->setCurrentIndex(0); // 默认选择第一个主题 "standard"
29 setTheme("standard"); // 应用默认主题
30 themeDescription->setText("Current theme: standard");
31
32 themeComboBox->changed().connect([=]() {
33 Wt::WString selectedTheme = themeComboBox->currentText();
34 setTheme(selectedTheme.toUTF8()); // 应用选中的主题
35 themeDescription->setText("Current theme: " + selectedTheme);
36 });
37 }
38 };

这段代码创建了一个下拉列表框,用户可以选择不同的内置主题。当主题选择改变时,应用会动态切换主题。

7.2.3 自定义主题

如果内置主题不能满足需求,开发者可以创建自定义主题。自定义主题通常包括以下步骤:

创建主题目录 (Create Theme Directory): 在 Wt 应用的 public 目录下创建一个新的目录,作为自定义主题的目录,例如 mytheme/

添加 CSS 样式表 (Add CSS Stylesheet): 在主题目录下创建一个 CSS 样式表文件,例如 mytheme/theme.css。在该文件中定义自定义主题的样式规则。

添加主题资源 (Add Theme Resources): 主题可能需要一些图片、字体或其他资源文件。将这些资源文件也放置在主题目录下。

在 Wt 应用中注册自定义主题 (Register Custom Theme in Wt Application): 使用 Wt::WApplication::setTheme() 方法应用自定义主题。主题名称就是主题目录名。

示例代码:应用自定义主题 (Example Code: Applying Custom Theme):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2
3 class CustomThemeExample : public Wt::WApplication
4 {
5 public:
6 CustomThemeExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
7 {
8 setTitle("Custom Theme Example");
9
10 setTheme("mytheme"); // 应用自定义主题 "mytheme"
11 }
12 };

需要确保在 public 目录下创建了 mytheme/ 目录,并在其中包含了 theme.css 文件和可能的其他资源文件。

自定义主题 CSS 样式表 (public/mytheme/theme.css) 示例:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 /* 自定义主题样式 */
2 body {
3 font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
4 background-color: #f8f8f8;
5 color: #333;
6 }
7
8 .WPushButton {
9 background-color: #007bff;
10 color: white;
11 border: none;
12 border-radius: 3px;
13 padding: 8px 15px;
14 }
15
16 .WPushButton:hover {
17 background-color: #0056b3;
18 }

自定义主题可以完全控制应用的视觉风格,使其与品牌形象或设计需求完美契合。

7.3 响应式设计 (Responsive Design)

响应式设计 (Responsive Design) 是一种 Web 设计方法,旨在使网页在各种设备 (例如桌面电脑、平板电脑、智能手机) 和不同屏幕尺寸下都能呈现最佳的用户体验。在移动设备普及的今天,响应式设计已成为 Web 开发的标配。

7.3.1 viewport 设置

要启用响应式设计,首先需要在 HTML 文档的 <head> 标签中设置 viewport meta 标签。Wt 框架会自动处理 viewport 设置,无需开发者手动添加。但了解 viewport 的作用有助于理解响应式设计的原理。

viewport meta 标签示例:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 <meta name="viewport" content="width=device-width, initial-scale=1.0">
  • width=device-width: 设置 viewport 宽度为设备宽度,确保网页在不同设备上以设备像素宽度渲染。
  • initial-scale=1.0: 设置初始缩放比例为 1.0,防止页面初始加载时被缩小。

7.3.2 CSS 媒体查询 (Media Queries)

CSS 媒体查询 (Media Queries) 是响应式设计的核心技术。媒体查询允许根据设备的特性 (例如屏幕宽度、屏幕高度、设备类型、屏幕分辨率等) 应用不同的 CSS 样式规则。

常用的媒体查询特性 (Common Media Query Features):

  • max-width: 最大屏幕宽度。当屏幕宽度小于或等于指定值时应用样式。
  • min-width: 最小屏幕宽度。当屏幕宽度大于或等于指定值时应用样式。
  • orientation: 设备方向 (portrait: 竖屏, landscape: 横屏)。
  • device-width: 设备屏幕宽度。
  • device-height: 设备屏幕高度。

示例代码:使用媒体查询实现响应式布局 (Example Code: Using Media Queries for Responsive Layout):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 /* 默认样式 (桌面端) */
2 .container {
3 display: flex; /* 使用 Flexbox 布局 */
4 }
5
6 .sidebar {
7 width: 25%; /* 侧边栏宽度 25% */
8 }
9
10 .content {
11 width: 75%; /* 内容区域宽度 75% */
12 }
13
14 /* 媒体查询:屏幕宽度小于 768px 时 (移动端) */
15 @media screen and (max-width: 768px) {
16 .container {
17 flex-direction: column; /* 垂直排列 */
18 }
19
20 .sidebar, .content {
21 width: 100%; /* 侧边栏和内容区域宽度 100% */
22 }
23 }

这段 CSS 代码定义了一个两列布局 (侧边栏 + 内容区域),默认情况下 (桌面端) 水平排列。当屏幕宽度小于 768px 时 (移动端),使用媒体查询将布局改为垂直排列,侧边栏和内容区域都占据 100% 宽度,实现响应式布局。

7.3.3 Wt 布局的响应式调整

Wt 框架的布局管理器 (例如 Wt::WBoxLayout, Wt::WGridLayout) 可以与 CSS 媒体查询结合使用,实现更复杂的响应式布局。

例如,可以使用 Wt::WContainerWidget 和 CSS 类名,结合媒体查询,动态地改变布局管理器的方向、组件的排列方式等。

示例代码:使用 WContainerWidget 和媒体查询实现响应式布局 (Example Code: Responsive Layout with WContainerWidget and Media Queries):

C++ 代码:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WContainerWidget.h>
3 #include <Wt/WHBoxLayout.h>
4 #include <Wt/WVBoxLayout.h>
5 #include <Wt/WText.h>
6
7 class ResponsiveLayoutExample : public Wt::WApplication
8 {
9 public:
10 ResponsiveLayoutExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
11 {
12 setTitle("Responsive Layout Example");
13
14 Wt::WContainerWidget *mainContainer = new Wt::WContainerWidget();
15 mainContainer->setStyleClass("responsive-container"); // 设置 CSS 类名
16 root()->addWidget(mainContainer);
17
18 Wt::WHBoxLayout *horizontalLayout = new Wt::WHBoxLayout();
19 Wt::WVBoxLayout *verticalLayout = new Wt::WVBoxLayout();
20
21 Wt::WText *sidebar = new Wt::WText("Sidebar");
22 sidebar->setStyleClass("sidebar");
23 Wt::WText *content = new Wt::WText("Content");
24 content->setStyleClass("content");
25
26 horizontalLayout->addWidget(sidebar);
27 horizontalLayout->addWidget(content);
28 verticalLayout->addWidget(sidebar);
29 verticalLayout->addWidget(content);
30
31 mainContainer->setLayout(horizontalLayout); // 初始布局为水平布局
32 }
33 };

CSS 样式表 (style.css):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 .responsive-container {
2 display: block; /* 默认 display: block */
3 }
4
5 .sidebar {
6 width: 25%;
7 background-color: #eee;
8 padding: 20px;
9 }
10
11 .content {
12 width: 75%;
13 padding: 20px;
14 }
15
16 /* 媒体查询:屏幕宽度小于 768px 时 */
17 @media screen and (max-width: 768px) {
18 .responsive-container {
19 display: flex; /* 修改为 flex 布局 */
20 flex-direction: column; /* 垂直排列 */
21 }
22
23 .sidebar, .content {
24 width: 100%;
25 }
26 }

在这个例子中,responsive-container 组件初始使用默认的 block 布局。在 CSS 媒体查询中,当屏幕宽度小于 768px 时,将 responsive-containerdisplay 属性修改为 flex,并设置为垂直排列,从而实现响应式布局切换。

通过本章的学习,你已经掌握了 Wt 框架的样式和主题功能,包括 CSS 样式表集成、主题的使用与定制以及响应式设计。这些知识将帮助你创建美观、专业、且在各种设备上都能良好运行的 Web 应用。 😊 在接下来的章节中,我们将继续学习 Wt 的国际化本地化、部署优化等方面的内容。

8. chapter 8:Wt 国际化与本地化 (Internationalization and Localization)

在全球化的 Web 环境中,国际化 (Internationalization, i18n)本地化 (Localization, l10n) 是至关重要的。国际化是指设计和开发软件应用程序,使其能够适应不同的语言和文化区域,而无需进行工程上的修改。本地化是指将国际化后的应用程序适配到特定的语言、文化和技术环境。Wt 框架提供了完善的国际化和本地化支持,帮助开发者构建面向全球用户的 Web 应用。本章将深入探讨 Wt 的国际化与本地化功能,包括多语言支持和本地化资源管理。

8.1 多语言支持 (Multi-language Support)

多语言支持 (Multi-language Support) 是国际化的核心组成部分,它允许应用程序以不同的语言显示用户界面文本和内容。Wt 框架通过资源文件、消息翻译和语言环境 (Locale) 管理等机制,实现了强大的多语言支持。

8.1.1 资源文件与消息翻译 (Resource Files and Message Translation)

Wt 使用 资源文件 (Resource Files) 来存储应用程序的本地化文本。资源文件通常是文本文件,例如 .properties.po 文件,其中包含了源语言文本及其对应的目标语言翻译。Wt 框架使用 tr() (translate) 函数来从资源文件中查找并获取翻译后的文本。

资源文件格式 (Resource File Format):

Wt 主要使用 .properties 格式的资源文件。每行定义一个消息键值对,格式为 key=value

示例资源文件 (messages.properties - 默认语言,例如英语):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 hello.world=Hello, World!
2 greeting.user=Hello, {0}!
3 button.submit=Submit

示例资源文件 (messages_zh.properties - 中文翻译):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 hello.world=你好,世界!
2 greeting.user=你好,{0}!
3 button.submit=提交

资源文件命名约定:messages_[语言代码].properties,例如 messages_zh.properties 表示中文资源文件,messages_fr.properties 表示法语资源文件。默认语言资源文件命名为 messages.properties

使用 tr() 函数进行消息翻译 (Using tr() function for message translation):

在 C++ 代码中,使用 Wt::WString::tr(const char *key) 函数来获取消息的翻译文本。key 参数是消息的键名,对应资源文件中的 key 部分。

示例代码:使用 tr() 函数 (Example Code: Using tr() function):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WText.h>
3
4 class LocalizationExample : public Wt::WApplication
5 {
6 public:
7 LocalizationExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
8 {
9 setTitle("Localization Example");
10
11 Wt::WText *helloText = new Wt::WText(tr("hello.world")); // 翻译 "hello.world" 键的消息
12 root()->addWidget(helloText);
13 }
14 };

当应用程序运行时,Wt 框架会根据当前设置的 语言环境 (Locale) 加载对应的资源文件,并使用 tr() 函数查找键名对应的翻译文本。

8.1.2 语言环境 (Locale) 管理

语言环境 (Locale) 是一组用于表示特定地理区域的文化习俗和语言习惯的设置。Locale 信息包括语言代码、国家/地区代码、字符编码、日期时间格式、数字格式、货币格式等。Wt 框架使用 Wt::WLocale 类来管理 Locale 信息,并根据 Locale 设置应用程序的语言和本地化格式。

设置应用程序 Locale (Setting Application Locale):

通过 Wt::WApplication::setLocale(const WLocale& locale) 方法: 在代码中显式设置应用程序的 Locale。通常在 WApplication 的构造函数或初始化代码中设置。

示例代码:设置 Locale 为中文 (Example Code: Setting Locale to Chinese):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WLocale.h>
3
4 class LocalizationExample : public Wt::WApplication
5 {
6 public:
7 LocalizationExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
8 {
9 setTitle("Localization Example");
10
11 Wt::WLocale chineseLocale("zh_CN"); // 创建中文 Locale 对象 (中国大陆)
12 setLocale(chineseLocale); // 设置应用程序 Locale 为中文
13 }
14 };

通过 Wt::WApplication::setPreferredLanguages(const std::vector<std::string>& languages) 方法: 设置应用程序的首选语言列表。Wt 框架会根据用户的浏览器 Accept-Language 头信息和首选语言列表,自动选择合适的 Locale。

示例代码:设置首选语言列表 (Example Code: Setting Preferred Languages):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <vector>
3 #include <string>
4
5 class LocalizationExample : public Wt::WApplication
6 {
7 public:
8 LocalizationExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
9 {
10 setTitle("Localization Example");
11
12 std::vector<std::string> preferredLanguages = {"zh-CN", "en-US", "en"}; // 设置首选语言列表:中文 (中国大陆), 英语 (美国), 英语
13 setPreferredLanguages(preferredLanguages); // 设置首选语言列表
14 }
15 };

Wt 框架会按照首选语言列表的顺序,以及浏览器 Accept-Language 头信息中指定的语言偏好,查找可用的资源文件。例如,如果 Accept-Language 头信息包含 zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,Wt 会首先尝试加载 messages_zh_CN.properties,如果找不到,则尝试 messages_zh.properties,然后是 messages_en_US.properties,最后是 messages_en.properties,如果以上资源文件都找不到,则使用默认的 messages.properties 文件。

8.1.3 消息参数与占位符 (Message Arguments and Placeholders)

在本地化消息中,有时需要插入动态内容,例如用户名、订单号、日期等。Wt 的 tr() 函数支持 消息参数 (Message Arguments)占位符 (Placeholders),可以在翻译文本中预留占位符,然后在运行时动态地填充参数。

使用占位符 (Using Placeholders):

在资源文件中,可以使用 {0}, {1}, {2} 等占位符来表示参数的位置。

示例资源文件 (messages.properties):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 greeting.user=Hello, {0}! Welcome to our website.
2 order.confirmation=Your order #{0} has been confirmed on {1}.

使用 arg() 方法填充参数 (Using arg() method to fill placeholders):

Wt::WString::tr() 函数返回的是 Wt::WMessage 对象。Wt::WMessage 类提供了 arg() 方法,用于填充占位符。

示例代码:使用 arg() 方法填充占位符 (Example Code: Using arg() method to fill placeholders):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WText.h>
3 #include <Wt/WString>
4
5 class LocalizationExample : public Wt::WApplication
6 {
7 public:
8 LocalizationExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
9 {
10 setTitle("Localization Example");
11
12 Wt::WString username = "John Doe";
13 Wt::WText *greetingText = new Wt::WText(tr("greeting.user").arg(username)); // 填充 {0} 占位符
14
15 Wt::WString orderId = "123456";
16 Wt::WString orderDate = "2024-01-01";
17 Wt::WText *orderConfirmationText = new Wt::WText(tr("order.confirmation").arg(orderId).arg(orderDate)); // 填充 {0} 和 {1} 占位符
18
19 root()->addWidget(greetingText);
20 root()->addWidget(orderConfirmationText);
21 }
22 };

tr("greeting.user").arg(username) 会将 username 的值填充到 greeting.user 消息中的 {0} 占位符位置。arg() 方法支持多种参数类型,例如 Wt::WString, int, double 等。

8.1.4 运行时语言切换 (Runtime Language Switching)

Wt 应用支持在运行时动态切换语言。可以通过以下步骤实现运行时语言切换功能:

创建语言选择组件 (Create Language Selection Widget): 例如使用 Wt::WComboBox 下拉列表框,列出可用的语言选项。

响应语言选择事件 (Handle Language Selection Event): 当用户选择新的语言时,获取选中的语言代码。

更新应用程序 Locale (Update Application Locale): 根据选中的语言代码,创建新的 Wt::WLocale 对象,并使用 Wt::WApplication::setLocale() 方法更新应用程序的 Locale。

刷新用户界面 (Refresh User Interface): 更新 Locale 后,需要刷新用户界面,使新的语言设置生效。可以重新加载页面或更新需要本地化的组件文本。

示例代码:运行时语言切换 (Example Code: Runtime Language Switching):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WComboBox.h>
3 #include <Wt/WText.h>
4 #include <Wt/WVBoxLayout.h>
5 #include <Wt/WLocale.h>
6
7 class LocalizationExample : public Wt::WApplication
8 {
9 public:
10 LocalizationExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
11 {
12 setTitle("Localization Example");
13
14 Wt::WVBoxLayout *vbox = new Wt::WVBoxLayout();
15 Wt::WComboBox *languageComboBox = new Wt::WComboBox();
16 Wt::WText *helloText = new Wt::WText();
17 vbox->addWidget(languageComboBox);
18 vbox->addWidget(helloText);
19 root()->setLayout(vbox);
20
21 // 添加语言选项
22 languageComboBox->addItem("English", "en"); // 显示文本 "English", 语言代码 "en"
23 languageComboBox->addItem("中文", "zh"); // 显示文本 "中文", 语言代码 "zh"
24 languageComboBox->setCurrentIndex(0); // 默认选择英语
25
26 updateUI("en"); // 初始加载英语界面
27
28 languageComboBox->changed().connect([=]() {
29 Wt::WString selectedLanguageCode = languageComboBox->currentValue(); // 获取选中的语言代码
30 updateUI(selectedLanguageCode.toUTF8()); // 更新界面语言
31 });
32 }
33
34 private:
35 void updateUI(const std::string& languageCode)
36 {
37 Wt::WLocale newLocale(languageCode + "_US"); // 创建新的 Locale 对象 (例如 "en_US", "zh_US")
38 setLocale(newLocale); // 设置应用程序 Locale
39
40 Wt::WComboBox *languageComboBox = root()->find<Wt::WComboBox>(); // 获取语言选择 ComboBox
41 Wt::WText *helloText = root()->find<Wt::WText>(); // 获取 HelloText 组件
42
43 if (helloText) {
44 helloText->setText(tr("hello.world")); // 更新 HelloText 的文本
45 }
46
47 if (languageComboBox) {
48 if (languageCode == "en") {
49 languageComboBox->setCurrentIndex(0); // 设置 ComboBox 选中英语
50 } else if (languageCode == "zh") {
51 languageComboBox->setCurrentIndex(1); // 设置 ComboBox 选中中文
52 }
53 }
54 }
55 };

这段代码创建了一个语言选择下拉列表框,当用户选择新的语言时,updateUI() 方法会更新应用程序的 Locale,并刷新界面文本。

8.2 本地化资源管理 (Localization Resource Management)

本地化资源管理 (Localization Resource Management) 涉及到组织、管理和维护应用程序的本地化资源,例如资源文件、图片、日期时间格式、数字格式等。良好的资源管理对于构建可维护、可扩展的国际化应用程序至关重要。

8.2.1 资源文件组织 (Resource File Organization)

推荐将资源文件组织在项目目录的特定位置,例如 locales/ 目录下,并按照语言代码创建子目录。

示例目录结构:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 my-wt-app/
2 ├── src/
3 │ ├── ... (C++ 源文件)
4 ├── public/
5 │ ├── ... (静态资源文件)
6 │ └── locales/
7 │ ├── messages.properties (默认语言资源文件)
8 │ ├── messages_en.properties (英语资源文件)
9 │ ├── messages_zh.properties (中文资源文件)
10 │ ├── messages_fr.properties (法语资源文件)
11 │ └── ... (其他语言资源文件)
12 ├── CMakeLists.txt
13 └── ...

在 CMakeLists.txt 文件中,需要配置 Wt 资源路径,以便 Wt 框架能够找到资源文件。

示例 CMakeLists.txt 配置:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 # ...
2 set(Wt_RESOURCES_DIR ${CMAKE_SOURCE_DIR}/public) # 设置 Wt 资源根目录为 public 目录
3 # ...

8.2.2 日期、时间、数字格式本地化 (Date, Time, Number Format Localization)

Wt 框架的 Wt::WLocale 类提供了本地化日期、时间、数字格式的功能。可以使用 Wt::WLocale 的方法获取当前 Locale 的格式设置,并使用 Wt::WDateTime, Wt::WDate, Wt::WTime 等类进行格式化输出。

常用本地化格式方法 (Common Localization Format Methods):

  • Wt::WLocale::dateFormat(DateFormat style = ShortFormat) const: 获取当前 Locale 的日期格式。style 参数指定日期格式的风格 (例如 ShortFormat, LongFormat, বিস্তারিত...)。
  • Wt::WLocale::timeFormat(TimeFormat style = ShortFormat) const: 获取当前 Locale 的时间格式。style 参数指定时间格式的风格。
  • Wt::WLocale::dateTimeFormat(DateFormat dateStyle = ShortFormat, TimeFormat timeStyle = ShortFormat) const: 获取当前 Locale 的日期时间格式。
  • Wt::WLocale::numberOptions() const: 获取当前 Locale 的数字格式选项,例如小数点分隔符、千位分隔符等。

示例代码:本地化日期时间格式 (Example Code: Localized Date and Time Format):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WText.h>
3 #include <Wt/WVBoxLayout.h>
4 #include <Wt/WDateTime.h>
5 #include <Wt/WLocale.h>
6
7 class LocalizationExample : public Wt::WApplication
8 {
9 public:
10 LocalizationExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
11 {
12 setTitle("Localization Example");
13
14 Wt::WVBoxLayout *vbox = new Wt::WVBoxLayout();
15 Wt::WText *dateTimeText = new Wt::WText();
16 vbox->addWidget(dateTimeText);
17 root()->setLayout(vbox);
18
19 Wt::WDateTime now = Wt::WDateTime::currentDateTime();
20 Wt::WLocale currentLocale = locale(); // 获取当前应用程序 Locale
21
22 Wt::WString formattedDateTime = now.toString(currentLocale.dateTimeFormat(Wt::DateFormat::LongFormat, Wt::TimeFormat::LongFormat)); // 使用 Locale 的日期时间格式进行格式化
23
24 dateTimeText->setText("Current date and time: " + formattedDateTime);
25 }
26 };

这段代码获取当前日期时间,并使用当前 Locale 的长日期时间格式进行格式化输出。不同 Locale 下,日期时间的显示格式会有所不同。

8.2.3 RTL 布局支持 (Right-to-Left Layout Support)

对于一些从右向左书写的语言 (例如阿拉伯语、希伯来语),需要支持 RTL (Right-to-Left) 布局。Wt 框架提供了 RTL 布局支持,可以通过设置 Locale 和 CSS 样式来实现 RTL 布局。

启用 RTL 布局 (Enabling RTL Layout):

设置 Locale 为 RTL 语言: 例如设置 Locale 为 ar_SA (阿拉伯语 - 沙特阿拉伯) 或 he_IL (希伯来语 - 以色列)。

CSS 样式设置 direction: rtl;: 为需要 RTL 布局的组件或容器设置 CSS 样式 direction: rtl;

Wt 框架会自动处理一些基本的 RTL 布局镜像,例如文本对齐方式、滚动条位置等。但对于复杂的布局,可能需要手动调整 CSS 样式以实现正确的 RTL 效果。

示例代码:启用 RTL 布局 (Example Code: Enabling RTL Layout):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WText.h>
3 #include <Wt/WVBoxLayout.h>
4 #include <Wt/WLocale.h>
5
6 class LocalizationExample : public Wt::WApplication
7 {
8 public:
9 LocalizationExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
10 {
11 setTitle("Localization Example");
12
13 Wt::WVBoxLayout *vbox = new Wt::WVBoxLayout();
14 Wt::WText *rtlText = new Wt::WText(tr("hello.world")); // 翻译文本
15 rtlText->setStyleProperty("direction", "rtl"); // 设置 RTL 布局
16 vbox->addWidget(rtlText);
17 root()->setLayout(vbox);
18
19 Wt::WLocale arabicLocale("ar_SA"); // 设置 Locale 为阿拉伯语
20 setLocale(arabicLocale);
21 }
22 };

这段代码将 Locale 设置为阿拉伯语,并为 rtlText 组件设置 CSS 样式 direction: rtl;,使其文本从右向左显示。

通过本章的学习,你已经掌握了 Wt 框架的国际化与本地化功能,包括多语言支持和本地化资源管理。这些知识将帮助你构建面向全球用户的、多语言的 Web 应用。 😊 在接下来的章节中,我们将继续学习 Wt 的部署优化等方面的内容。

9. chapter 9:Wt 部署与性能优化 (Deployment and Performance Optimization)

9.1 服务器部署 (Server Deployment)

将 Wt (Web Toolkit) 应用部署到服务器是使其对外提供服务的关键步骤。合理的部署方案不仅能保证应用的稳定运行,还能提升性能和安全性。服务器部署涉及多个环节,包括选择合适的部署环境、部署方式、服务器配置以及具体的部署步骤。

9.1.1 部署环境 (Deployment Environments)

在软件开发生命周期中,通常会涉及多个部署环境,每个环境都有其特定的用途和配置:

开发环境 (Development Environment)
开发环境主要用于开发人员编写、测试和调试代码。通常开发环境部署在开发人员的本地机器上,配置相对简单,侧重于快速迭代和调试效率。例如,可以使用简单的单进程 Wt 服务器进行本地开发测试。

测试环境/预发布环境 (Staging Environment)
预发布环境是模拟生产环境的测试环境,用于在正式发布前进行全面的功能测试、性能测试和用户验收测试 (User Acceptance Testing, UAT)。预发布环境的配置应尽可能与生产环境保持一致,以确保测试的准确性。

生产环境 (Production Environment)
生产环境是最终用户访问的正式运行环境。生产环境需要具备高可用性、高性能、高安全性以及良好的可扩展性。生产环境的部署方案通常较为复杂,可能涉及负载均衡、反向代理、数据库集群等技术。

9.1.2 部署方式 (Deployment Options)

Wt 应用的部署方式多种多样,可以根据应用的规模、性能需求和预算选择合适的方案:

独立部署 (Standalone Deployment)
独立部署是最简单的部署方式,将 Wt 应用直接部署在服务器上,Wt 应用自身监听 HTTP 请求并处理所有客户端连接。这种方式适用于小型应用或开发测试环境。

反向代理部署 (Reverse Proxy Deployment)
反向代理服务器(例如 Nginx, Apache HTTP Server)位于 Wt 应用服务器之前,接收客户端的 HTTP 请求,并将请求转发给 Wt 应用服务器。反向代理可以提供负载均衡、SSL termination (安全套接层终端)、静态资源缓存、安全防护等功能,是生产环境常用的部署方式。

负载均衡部署 (Load Balancer Deployment)
对于高负载、高并发的应用,可以使用负载均衡器 (Load Balancer) 将客户端请求分发到多台 Wt 应用服务器上。负载均衡可以提高应用的吞吐量、可用性和可扩展性。负载均衡器通常与反向代理结合使用。

9.1.3 服务器配置 (Server Configuration)

服务器配置是部署过程中至关重要的一环,合理的配置能直接影响应用的性能、安全性和稳定性。常见的服务器配置项包括:

HTTP 监听器配置 (HTTP Listener Configuration)
配置 Wt 服务器监听的 IP 地址和端口号。生产环境通常监听 80 端口 (HTTP) 和 443 端口 (HTTPS)。

资源路径配置 (Resource Paths Configuration)
配置 Wt 应用的静态资源文件路径 (例如 CSS, JavaScript, 图片)。确保 Wt 应用能够正确访问和提供静态资源。

会话管理配置 (Session Management Configuration)
配置 Wt 应用的会话管理策略,例如会话超时时间、会话存储方式 (内存、文件、数据库) 等。生产环境应考虑使用持久化会话存储,以防止服务器重启导致会话丢失。

安全配置 (Security Configuration)
配置 HTTPS 加密传输,防止数据在传输过程中被窃听。配置防火墙规则,限制对服务器的访问。配置安全相关的 HTTP 头 (例如 Content Security Policy, HTTP Strict Transport Security)。

日志配置 (Logging Configuration)
配置 Wt 应用的日志级别、日志输出格式、日志存储位置等。详细的日志信息对于故障排查和性能分析至关重要。

9.1.4 部署步骤 (Deployment Steps)

通用的 Wt 应用部署步骤如下:

构建应用 (Build Application)
在部署服务器或构建环境中,使用 CMake 和 Make (或 Ninja) 构建 Wt 应用的可执行文件。确保构建配置与部署环境相匹配 (例如 Release 模式编译)。

配置应用 (Configure Application)
根据部署环境配置 Wt 应用。配置文件可以包括数据库连接信息、服务器监听地址、资源路径、会话管理配置、日志配置等。可以使用命令行参数、环境变量或独立的配置文件来传递配置信息。

上传应用和资源文件 (Upload Application and Resource Files)
将构建好的 Wt 应用可执行文件、配置文件、静态资源文件 (例如 public 目录) 上传到部署服务器的指定目录。可以使用 scp, rsync 或其他文件传输工具进行上传。

启动应用 (Startup Application)
在部署服务器上启动 Wt 应用。可以使用命令行直接运行可执行文件,也可以使用 systemd, supervisord 等进程管理工具来管理 Wt 应用的生命周期。

监控和测试 (Monitoring and Testing)
部署完成后,需要监控 Wt 应用的运行状态,例如 CPU 使用率、内存使用率、网络流量、错误日志等。进行功能测试和性能测试,确保应用部署成功且运行正常。

9.2 性能优化策略 (Performance Optimization Strategies)

性能优化是 Web 应用开发和部署中不可或缺的环节。对于 Wt 应用,性能优化可以从前端、后端以及 Wt 框架自身等多个层面进行。

9.2.1 前端优化 (Frontend Optimization)

前端优化主要关注客户端浏览器端的性能提升,减少页面加载时间和提高用户体验:

减少 HTTP 请求 (Minimize HTTP Requests)
合并 CSS 和 JavaScript 文件,使用 CSS Sprites (CSS 雪碧图) 减少图片请求,合理使用内联 CSS 和 JavaScript。

优化静态资源 (Optimize Static Resources)
压缩 CSS, JavaScript 和 HTML 文件 (Minification)。压缩图片 (Image Compression)。使用浏览器缓存 (Browser Caching) 和 HTTP 缓存头 (Cache-Control, Expires)。

使用 CDN (Content Delivery Network, 内容分发网络)
将静态资源 (例如 CSS, JavaScript, 图片, 字体文件) 部署到 CDN 上,利用 CDN 节点的地理位置优势,加速用户访问静态资源的速度。

延迟加载 (Lazy Loading)
对于非首屏内容 (例如图片、长列表),可以使用延迟加载技术,只在用户需要时才加载,减少初始页面加载时间。

代码优化 (Code Optimization)
优化 JavaScript 代码,避免阻塞主线程的操作。使用异步加载 JavaScript。

9.2.2 后端优化 (Backend Optimization)

后端优化主要关注 Wt 应用服务器端的性能提升,提高服务器的处理能力和响应速度:

高效的 C++ 代码 (Efficient C++ Code)
编写高效的 C++ 代码,避免不必要的内存分配和拷贝,减少计算复杂度。使用性能分析工具 (Profiler) 找出性能瓶颈并进行优化。

数据库优化 (Database Optimization)
优化数据库查询语句 (SQL Query Optimization),使用索引 (Index) 加速查询,合理设计数据库表结构,使用数据库连接池 (Database Connection Pooling) 提高数据库访问效率。

缓存 (Caching)
使用缓存技术 (例如内存缓存 Redis, Memcached 或本地缓存) 缓存热点数据,减少数据库访问次数。缓存可以应用于页面片段、数据查询结果、计算结果等。

异步操作 (Asynchronous Operations)
对于耗时的操作 (例如 I/O 操作、网络请求、数据库查询),使用异步操作,避免阻塞主线程,提高服务器的并发处理能力。

连接池 (Connection Pooling)
使用数据库连接池、HTTP 连接池等连接池技术,复用连接,减少连接建立和断开的开销。

9.2.3 Wt 框架特性优化 (Wt Specific Optimization)

除了通用的前端和后端优化策略,还可以利用 Wt 框架自身的特性进行性能优化:

组件复用 (Widget Reuse)
尽量复用 Widget 组件,避免频繁创建和销毁组件。对于重复使用的 UI 元素,可以使用模板 (Template) 或组件池 (Widget Pool) 进行管理。

高效渲染 (Efficient Rendering)
Wt 框架采用了高效的 UI 更新机制,只传输必要的 UI 更新指令,而不是整个 HTML 页面。开发者应避免不必要的 UI 组件更新,减少客户端和服务器之间的网络传输量。

服务器推送 (Server Push)
合理使用服务器推送技术 (例如 WebSocket, Server-Sent Events) 实时更新 UI,减少客户端轮询请求,降低服务器负载。

按需加载组件 (Lazy Loading Widgets)
对于初始页面不需要立即显示的组件 (例如隐藏的面板、对话框),可以使用延迟加载,只在需要时才创建和渲染组件,减少初始页面加载时间。

9.3 负载均衡 (Load Balancing)

负载均衡 (Load Balancing) 是将客户端请求分发到多个服务器上,以提高应用的可用性、可扩展性和性能的技术。在 Wt 应用的生产环境中,负载均衡通常是必不可少的。

9.3.1 负载均衡器类型 (Load Balancer Types)

负载均衡器可以分为硬件负载均衡器和软件负载均衡器两种类型:

硬件负载均衡器 (Hardware Load Balancer)
硬件负载均衡器是专门的硬件设备,具有高性能、高可靠性的特点。硬件负载均衡器通常提供更丰富的功能和更高的性能,但成本也较高。常见的硬件负载均衡器厂商包括 F5, Citrix, Radware 等。

软件负载均衡器 (Software Load Balancer)
软件负载均衡器是运行在通用服务器上的软件程序。软件负载均衡器具有灵活性高、成本低的优点,适用于中小型应用或云环境。常见的软件负载均衡器包括 Nginx, HAProxy, Apache HTTP Server (mod_proxy_balancer) 等。

9.3.2 负载均衡算法 (Load Balancing Algorithms)

负载均衡器使用不同的 负载均衡算法 (Load Balancing Algorithms) 来决定将请求分发到哪台服务器。常用的负载均衡算法包括:

轮询 (Round Robin)
轮询算法将请求依次分发到每台服务器。算法简单,但没有考虑服务器的实际负载情况。

加权轮询 (Weighted Round Robin)
加权轮询算法为每台服务器分配不同的权重,权重高的服务器接收更多的请求。可以根据服务器的性能配置不同的权重。

最少连接 (Least Connections)
最少连接算法将请求分发到当前连接数最少的服务器。可以根据服务器的当前负载动态调整请求分发。

IP Hash (IP Hash)
IP Hash 算法根据客户端 IP 地址的 Hash 值将请求分发到固定的服务器。可以保证来自同一 IP 地址的请求始终被路由到同一台服务器 (Session Persistence, 会话保持)。

URL Hash (URL Hash)
URL Hash 算法根据请求 URL 的 Hash 值进行分发。

9.3.3 负载均衡环境下的会话保持 (Session Persistence in Load Balanced Environments)

在负载均衡环境中,由于客户端请求可能被分发到不同的服务器上,需要考虑 会话保持 (Session Persistence) 的问题,确保同一个用户的会话请求始终被路由到同一台服务器,以避免会话丢失或状态不一致。

常用的会话保持方案包括:

IP Hash 会话保持 (IP Hash Session Persistence): 如前所述,IP Hash 算法可以根据客户端 IP 地址将请求路由到同一台服务器,实现会话保持。但 IP Hash 会话保持可能导致负载不均衡,因为来自某些 IP 地址段的请求可能非常集中。

基于 Cookie 的会话保持 (Cookie-Based Session Persistence): 负载均衡器在首次接收到来自客户端的请求时,会选择一台服务器,并将服务器的标识信息写入 Cookie 中返回给客户端。后续来自同一客户端的请求,负载均衡器会读取 Cookie 中的服务器标识信息,并将请求路由到对应的服务器。

会话集中存储 (Centralized Session Storage): 将会话数据集中存储在共享的存储系统 (例如 Redis, Memcached, 数据库) 中。所有 Wt 应用服务器都从共享存储系统中读取和写入会话数据。会话集中存储方案可以实现真正的无状态 (Stateless) 应用服务器,具有更好的可扩展性和容错性,但实现复杂度较高。

9.3.4 健康检查与监控 (Health Checks and Monitoring)

在负载均衡环境中,负载均衡器需要定期对后端 Wt 应用服务器进行 健康检查 (Health Checks),检测服务器是否运行正常。如果检测到某台服务器发生故障,负载均衡器会自动将其从服务列表中移除,避免将请求分发到故障服务器。

同时,还需要对负载均衡器和 Wt 应用服务器进行 监控 (Monitoring),监控其性能指标 (例如请求数、响应时间、错误率、CPU 使用率、内存使用率、网络流量等),及时发现和解决性能问题或故障。

通过本章的学习,你已经掌握了 Wt 应用的部署与性能优化策略,包括服务器部署、性能优化以及负载均衡。这些知识将帮助你构建高性能、高可用、可扩展的 Wt Web 应用,并将其成功部署到生产环境。 😊 在接下来的章节中,我们将继续学习 Wt 的扩展与高级话题。

10. chapter 10:Wt 实战案例分析 (Case Studies)

为了更好地理解 Wt (Web Toolkit) 框架在实际项目中的应用,本章节将深入分析三个不同类型的实战案例。这些案例涵盖了企业级数据管理平台、在线协作工具以及实时监控系统,旨在展示 Wt 框架在构建各种复杂 Web 应用时的强大功能和灵活性。通过学习这些案例,你将能够更深入地掌握 Wt 的应用技巧,并为你的实际项目开发提供有益的参考。

10.1 案例一:企业级数据管理平台

企业级数据管理平台是现代企业运营的核心系统之一,用于集中管理和维护企业的关键数据资产。这类平台通常需要具备高安全性 (High Security)高可靠性 (High Reliability)强大的数据处理能力 (Powerful Data Processing Capabilities)灵活的用户权限管理 (Flexible User Rights Management) 等特点。Wt 框架非常适合构建这类企业级应用。

平台功能概述 (Platform Function Overview)

用户管理 (User Management): 用户注册、登录、权限管理、角色分配等功能。
数据字典管理 (Data Dictionary Management): 维护元数据信息,例如数据表的结构、字段描述、数据类型等。
数据录入与编辑 (Data Entry and Editing): 提供表单界面,允许用户录入、修改和删除数据。
数据查询与报表 (Data Query and Reporting): 支持多条件数据查询、数据过滤、排序和生成各种报表。
数据导出与导入 (Data Export and Import): 支持将数据导出为 CSV, Excel 等格式,以及从外部文件导入数据。
审计日志 (Audit Logs): 记录用户的操作日志,方便审计和追溯。

Wt 技术选型与应用 (Wt Technology Selection and Application)

▮▮▮▮ⓐ 用户界面 (User Interface)
使用 Wt::WContainerWidget 组织页面布局,使用 Wt::WForm 和各种表单组件 (例如 Wt::WLineEdit, Wt::WTextArea, Wt::WComboBox, Wt::WDateEdit) 构建数据录入和编辑界面。使用 Wt::WTable 组件展示数据表格,并支持排序、分页和过滤功能。使用 Wt::WTabWidgetWt::WStackedWidget 实现多标签页或向导式操作界面。

▮▮▮▮ⓑ 数据模型 (Data Model)
自定义 C++ 数据模型,或者使用 Wt::WStandardItemModel 结合数据库查询结果,作为 Wt::WTable 的数据源。使用 Wt::WSortFilterProxyModel 实现表格数据的排序和过滤。

▮▮▮▮ⓒ 数据库集成 (Database Integration)
使用 SOCI 或 Qt SQL 等 C++ 数据库库连接企业级数据库系统 (例如 PostgreSQL, Oracle, SQL Server)。使用数据库连接池提高数据库访问效率。使用 Wt::Dbo (Wt Database Objects,Wt 数据库对象) 进行更高级的数据库 ORM (对象关系映射) 操作 (可选)。

▮▮▮▮ⓓ 事件处理 (Event Handling)
使用信号与槽机制处理用户交互事件,例如按钮点击、表单提交、表格单元格点击等。在槽函数中编写业务逻辑代码,例如数据验证、数据保存、数据库查询等。

▮▮▮▮ⓔ 安全与权限 (Security and Permissions)
使用 Wt 的会话管理 (Session Management) 功能实现用户身份验证 (Authentication) 和会话管理。基于角色 (Role-Based Access Control, RBAC) 实现用户权限控制。对敏感数据进行加密存储和传输 (HTTPS)。

▮▮▮▮ⓕ 报表生成 (Report Generation)
可以使用 Wt 的绘图功能 (Wt::WPainter, Wt::WPaintedWidget) 自定义报表图表。也可以集成第三方报表库 (例如 ReportLab, JasperReports) 生成更复杂的报表。

案例特点与优势 (Case Features and Advantages)

高安全性: 服务端渲染架构,核心业务逻辑和数据处理在服务器端完成,降低了客户端安全风险。服务端会话管理和权限控制机制,保障数据安全。
高可靠性: C++ 语言的稳定性和 Wt 框架的成熟度,保证了平台的可靠运行。可使用负载均衡和集群部署提高系统可用性。
高性能: C++ 语言的高性能和 Wt 框架的高效渲染机制,保证了平台的高性能和快速响应。
易于维护: 组件化开发模式和 MVC 架构思想,使得代码结构清晰,易于维护和扩展。纯 C++ 开发,降低了技术栈复杂性。
跨浏览器兼容性: Wt 框架自动处理跨浏览器兼容性问题,减少了开发者的兼容性工作。

10.2 案例二:在线协作工具

在线协作工具在现代办公环境中扮演着越来越重要的角色,例如在线文档编辑、项目管理、团队沟通等。这类工具通常需要支持实时协作 (Real-time Collaboration)多用户并发 (Multi-user Concurrency)消息推送 (Message Push)富客户端交互体验 (Rich Client Interaction Experience) 等特点。Wt 框架的 WebSocket 和服务器推送功能非常适合构建实时协作应用。

工具功能概述 (Tool Function Overview)

实时文档协同编辑 (Real-time Document Co-editing): 允许多个用户同时编辑同一份文档,并实时同步编辑内容。
在线白板 (Online Whiteboard): 提供在线白板功能,支持多人实时绘图、标注和协作。
即时通讯 (Instant Messaging): 集成即时通讯功能,方便团队成员实时沟通和交流。
任务管理 (Task Management): 支持创建、分配、跟踪和管理任务,提高团队协作效率。
版本控制 (Version Control): 记录文档和白板的历史版本,方便回溯和版本管理。
权限控制 (Permission Control): 控制不同用户对文档、白板和任务的访问和编辑权限。

Wt 技术选型与应用 (Wt Technology Selection and Application)

▮▮▮▮ⓐ 实时通信 (Real-time Communication)
使用 Wt::WebSocketWt::WebSocketServer 实现客户端与服务器之间的双向实时通信。WebSocket 用于传输用户操作、文档内容变更、即时消息等实时数据。

▮▮▮▮ⓑ 服务器推送 (Server Push)
利用 WebSocket 的服务器推送功能,将文档变更、消息通知等实时推送给所有在线用户。实现毫秒级的实时同步效果。

▮▮▮▮ⓒ 用户界面 (User Interface)
使用 Wt::WTextArea 或富文本编辑器组件 (例如 CKEditor, TinyMCE 集成) 实现文档编辑界面。使用 Wt::WPaintedWidget 自定义在线白板组件,支持鼠标绘图、图形绘制等功能。使用 Wt::WList 或自定义组件实现消息列表和用户列表。

▮▮▮▮ⓓ 数据同步与一致性 (Data Synchronization and Consistency)
在服务器端维护文档和白板的共享数据模型。当用户在客户端进行编辑操作时,将操作指令通过 WebSocket 发送到服务器端。服务器端更新共享数据模型,并将更新后的数据推送给所有客户端。使用 Operational Transformation (OT, 操作转换)Conflict-free Replicated Data Types (CRDTs, 无冲突复制数据类型) 等算法解决多用户并发编辑时的数据冲突问题 (高级特性,可选)。

▮▮▮▮ⓔ 状态管理 (State Management)
使用 Wt 的会话管理 (Session Management) 功能管理用户会话和在线状态。在服务器端维护用户的在线列表,并实时更新和推送用户在线状态。

案例特点与优势 (Case Features and Advantages)

实时协作: WebSocket 和服务器推送技术,实现毫秒级实时数据同步,提供流畅的协作体验。
高并发: Wt 框架的高性能和异步非阻塞 I/O 模型,支持高并发用户连接和实时数据传输。
富客户端: Wt 组件库和自定义组件开发能力,可以构建功能丰富、交互性强的用户界面。
易于部署: Wt 应用可以部署在各种 Web 服务器和云平台上,部署灵活方便。
跨平台: Wt 应用可以运行在各种主流浏览器和操作系统上,实现跨平台访问。

10.3 案例三:实时监控系统

实时监控系统广泛应用于工业自动化、环境监测、交通管理、安全监控等领域。这类系统需要具备实时数据采集 (Real-time Data Acquisition)高吞吐量数据处理 (High-throughput Data Processing)实时数据可视化 (Real-time Data Visualization)告警处理 (Alarm Handling) 等特点。Wt 框架的服务器推送、数据模型和图形绘图功能非常适合构建实时监控系统。

系统功能概述 (System Function Overview)

数据采集 (Data Acquisition): 从各种传感器、设备或数据源实时采集监控数据。
数据处理 (Data Processing): 对采集到的数据进行清洗、转换、聚合和分析。
实时数据可视化 (Real-time Data Visualization): 使用图表、仪表盘、地图等可视化组件实时展示监控数据。
告警管理 (Alarm Management): 根据预设规则检测异常数据,并触发告警通知。
历史数据查询 (Historical Data Query): 支持查询和分析历史监控数据。
用户权限管理 (User Rights Management): 控制不同用户对监控数据的访问和操作权限。

Wt 技术选型与应用 (Wt Technology Selection and Application)

▮▮▮▮ⓐ 实时数据推送 (Real-time Data Push)
使用 Server-Sent Events (SSE) 或 WebSocket 将实时监控数据从服务器端推送到客户端浏览器。SSE 适用于单向数据推送,WebSocket 适用于双向交互和更复杂的数据传输场景。

▮▮▮▮ⓑ 数据模型 (Data Model)
创建自定义 C++ 数据模型,用于存储和管理实时监控数据。数据模型可以与数据采集模块对接,实时更新数据。

▮▮▮▮ⓒ 数据可视化 (Data Visualization)
使用 Wt 的绘图功能 (Wt::WPainter, Wt::WPaintedWidget) 自定义实时图表组件,例如折线图、柱状图、饼图、仪表盘等。也可以集成第三方 JavaScript 图表库 (例如 Chart.js, ECharts, Highcharts) 进行更丰富的可视化展示。使用 Wt::WMap 组件集成地图功能,展示地理位置相关的监控数据 (例如 GPS 轨迹、地理热力图)。

▮▮▮▮ⓓ 仪表盘 (Dashboard)
使用 Wt::WContainerWidget 和布局管理器 (例如 Wt::WGridLayout, Wt::WBorderLayout) 构建灵活可定制的仪表盘界面。仪表盘上可以集成多个图表、仪表、地图等组件,集中展示关键监控指标。

▮▮▮▮ⓔ 告警处理 (Alarm Handling)
在服务器端实现告警规则引擎,根据预设规则 (例如阈值、趋势分析) 检测异常数据。当触发告警时,通过服务器推送 (SSE 或 WebSocket) 将告警信息实时推送给客户端,并在用户界面上进行告警提示 (例如颜色变化、声音告警、消息通知)。

案例特点与优势 (Case Features and Advantages)

实时性: 服务器推送技术保证监控数据的实时更新,用户可以及时掌握系统状态。
高吞吐量: Wt 框架的高性能和异步处理能力,支持高吞吐量实时数据处理和推送。
可视化: 丰富的图表组件和地图集成,提供直观的数据可视化展示,帮助用户快速理解监控数据。
可扩展性: Wt 应用易于扩展,可以方便地添加新的监控指标、可视化组件和告警规则。
易于集成: Wt 应用可以与各种数据采集系统和后端数据存储系统集成,构建完整的实时监控解决方案。

通过以上三个案例的分析,我们可以看到 Wt 框架在构建各种类型的 Web 应用时都展现出了强大的能力和独特的优势。无论是企业级应用、协作工具还是实时系统,Wt 框架都能提供高效、稳定、安全且易于维护的解决方案。希望这些案例能够帮助你更好地理解和应用 Wt 框架。 😊 接下来,我们将进入最后一个章节,探讨 Wt 的扩展与高级话题。

11. chapter 11:Wt 扩展与高级话题

在前述章节中,我们系统地学习了 Wt (Web Toolkit) 框架的核心功能和应用技巧。为了更全面地掌握 Wt,本章节将深入探讨 Wt 的扩展机制和一些高级话题,包括自定义组件开发、Wt 与第三方库集成以及 Wt 的未来发展趋势。这些内容将帮助你进一步提升 Wt 开发技能,并了解 Wt 的更广阔的应用前景。

11.1 自定义组件开发 (Custom Widget Development)

Wt 框架提供了丰富的内置组件库,但在实际开发中,有时需要创建 自定义组件 (Custom Widgets) 以满足特定的业务需求或界面风格。Wt 框架具有良好的扩展性,允许开发者轻松创建和集成自定义组件。

11.1.1 自定义组件的基本步骤 (Basic Steps for Custom Widget Development)

开发自定义组件通常需要以下步骤:

继承 Wt::WWidget 类 (Inherit from Wt::WWidget)
自定义组件类必须直接或间接地继承自 Wt::WWidget 类。WWidget 是所有 Wt 组件的基类,提供了组件的基本接口和功能。

实现构造函数 (Implement Constructor)
在自定义组件的构造函数中,调用父类 Wt::WWidget 的构造函数,并进行组件的初始化操作,例如设置组件的初始状态、创建子组件、设置布局等。

实现 paintEvent() 方法 (Implement paintEvent() method) (可选)
如果自定义组件需要进行自定义绘制 (例如绘制图形、复杂文本),需要重写 paintEvent(WPaintEvent event) 方法。在 paintEvent() 方法中使用 Wt::WPainter 进行绘图操作。对于基于现有组件组合的复合组件,通常不需要重写 paintEvent() 方法。

添加子组件 (Add Child Widgets) (可选)
自定义组件可以包含其他 Wt 组件作为子组件,构建更复杂的 UI 结构。使用 addWidget() 方法将子组件添加到自定义组件中。可以使用布局管理器 (Wt::WBoxLayout, Wt::WGridLayout 等) 管理子组件的布局。

定义信号 (Define Signals) (可选)
如果自定义组件需要发出自定义的信号,需要在组件类中定义信号。使用 Wt::Signal 模板类定义信号,并提供信号发射方法 (通常以 emit 开头)。

实现事件处理方法 (Implement Event Handling Methods) (可选)
如果自定义组件需要响应特定的用户事件 (例如鼠标事件、键盘事件),需要重写相应的事件处理方法,例如 mouseClickedEvent(WMouseEvent event), keyPressEvent(WKeyEvent event) 等。

注册组件 (Register Component) (可选,如果需要通过 UI Designer 可视化编辑)
如果希望自定义组件能够在 Wt UI Designer 中可视化编辑,需要注册自定义组件的元数据信息。对于纯代码开发的组件,通常不需要注册。

11.1.2 示例:自定义计数器组件 (Example: Custom Counter Widget)

自定义计数器组件 CounterWidget 代码:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WWidget.h>
2 #include <Wt/WPushButton.h>
3 #include <Wt/WText.h>
4 #include <Wt/WHBoxLayout.h>
5 #include <Wt/Signal.h>
6
7 class CounterWidget : public Wt::WWidget
8 {
9 public:
10 CounterWidget(Wt::WContainerWidget *parent = nullptr) : Wt::WWidget(parent), count_(0)
11 {
12 // 设置布局
13 WHBoxLayout *layout = setLayout(std::make_unique<WHBoxLayout>());
14
15 // 创建子组件
16 incrementButton_ = layout->addWidget(std::make_unique<WPushButton>("Increment"));
17 countText_ = layout->addWidget(std::make_unique<WText>("Count: 0"));
18
19 // 连接信号和槽
20 incrementButton_->clicked().connect(this, &CounterWidget::incrementCount);
21 }
22
23 Wt::Signal<> &valueChanged() { return valueChangedSignal_; } // 获取 valueChanged 信号
24
25 private:
26 void incrementCount()
27 {
28 count_++;
29 countText_->setText("Count: " + std::to_string(count_));
30 valueChangedSignal_.emit(); // 发射 valueChanged 信号
31 }
32
33 WPushButton *incrementButton_;
34 WText *countText_;
35 int count_;
36 Wt::Signal<> valueChangedSignal_; // 自定义信号
37 };

在应用中使用自定义计数器组件:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include "CounterWidget.h" // 包含自定义组件头文件
3
4 class CustomWidgetExample : public Wt::WApplication
5 {
6 public:
7 CustomWidgetExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
8 {
9 setTitle("Custom Widget Example");
10
11 CounterWidget *counter1 = new CounterWidget();
12 CounterWidget *counter2 = new CounterWidget();
13
14 root()->addWidget(counter1);
15 root()->addWidget(counter2);
16
17 counter1->valueChanged().connect([=]() { // 连接 counter1 的 valueChanged 信号
18 Wt::log("info") << "Counter 1 value changed!";
19 });
20
21 counter2->valueChanged().connect([=]() { // 连接 counter2 的 valueChanged 信号
22 Wt::log("info") << "Counter 2 value changed!";
23 });
24 }
25 };

这个例子创建了一个简单的 CounterWidget 自定义组件,包含一个 "Increment" 按钮和一个显示计数值的文本标签。点击按钮计数器值会递增,并发出 valueChanged() 信号。

11.1.3 复合组件与继承 (Composite Widgets and Inheritance)

自定义组件可以分为两种类型:

复合组件 (Composite Widget)
复合组件是通过组合现有 Wt 组件来构建的。例如,CounterWidget 就是一个复合组件,它组合了 Wt::WPushButtonWt::WText 组件。复合组件的开发通常比较简单,主要通过布局管理和信号槽连接来实现组件的功能。

继承组件 (Inherited Widget)
继承组件是通过继承 Wt 框架的现有组件类 (例如 Wt::WPushButton, Wt::WContainerWidget) 并重写其方法来定制组件的行为或外观。继承组件的开发相对复杂,需要深入理解 Wt 组件的内部机制。

在实际开发中,复合组件更常用,因为它更简单、灵活,并且能够充分利用 Wt 框架提供的现有组件。只有在需要深度定制组件行为或外观,或者需要实现全新的 UI 元素时,才需要考虑继承组件的开发。

11.2 Wt 与第三方库集成 (Integration with Third-party Libraries)

Wt 框架具有良好的互操作性,可以方便地与各种第三方 C++ 库和 JavaScript 库集成,扩展 Wt 应用的功能。

11.2.1 集成 C++ 第三方库 (Integration with C++ Third-party Libraries)

Wt 应用可以使用任何标准的 C++ 库。集成 C++ 第三方库通常只需要在 CMakeLists.txt 文件中添加库的依赖,并在 C++ 代码中包含库的头文件即可。

常见的 C++ 第三方库集成场景:

数据库库 (Database Libraries): 例如 SOCI, Qt SQL, libpqxx, mysql-connector-cpp, sqlitecpp 等,用于数据库访问和数据持久化。第五章已详细介绍数据库集成。

图形图像处理库 (Graphics and Image Processing Libraries): 例如 OpenCV, ImageMagick, Cairo, FreeType 等,用于图像处理、图形渲染、字体处理等。可以扩展 Wt 的图形绘图功能。

科学计算库 (Scientific Computing Libraries): 例如 Eigen, Armadillo, Boost.Math, GSL (GNU Scientific Library) 等,用于科学计算、数值分析、线性代数、统计分析等。可以构建科学计算和数据分析相关的 Web 应用。

网络通信库 (Network Communication Libraries): 例如 Boost.Asio, libcurl, ZeroMQ, Protocol Buffers, gRPC 等,用于网络编程、高性能网络通信、分布式系统开发等。可以扩展 Wt 的网络通信能力。

JSON 和 XML 解析库 (JSON and XML Parsing Libraries): 例如 rapidjson, jsoncpp, Boost.JSON, pugixml, TinyXML-2 等,用于 JSON 和 XML 数据的解析和生成。方便与其他 Web 服务或数据源进行数据交换。

示例:集成 rapidjson JSON 库 (Example: Integration with rapidjson JSON Library)

  1. 添加 rapidjson 依赖到 CMakeLists.txt:
1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 find_package(RapidJSON REQUIRED) # 查找 rapidjson 库
2
3 add_executable(myapp main.cpp)
4 target_link_libraries(myapp Wt::Wt RapidJSON::RapidJSON) # 链接 rapidjson 库
  1. 在 C++ 代码中使用 rapidjson:
1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include <Wt/WText.h>
3 #include <Wt/WVBoxLayout.h>
4 #include <rapidjson/document.h>
5 #include <rapidjson/writer.h>
6 #include <rapidjson/stringbuffer.h>
7
8 class ThirdPartyLibExample : public Wt::WApplication
9 {
10 public:
11 ThirdPartyLibExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
12 {
13 setTitle("Third-party Library Example");
14
15 Wt::WVBoxLayout *vbox = new Wt::WVBoxLayout();
16 Wt::WText *jsonText = new Wt::WText();
17 vbox->addWidget(jsonText);
18 root()->setLayout(vbox);
19
20 // 使用 rapidjson 创建 JSON 文档
21 rapidjson::Document document;
22 document.SetObject();
23 rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
24 document.AddMember("name", rapidjson::StringRef("Wt Application"), allocator);
25 document.AddMember("version", 4, allocator);
26
27 // 将 JSON 文档转换为字符串
28 rapidjson::StringBuffer buffer;
29 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
30 document.Accept(writer);
31 std::string jsonString = buffer.GetString();
32
33 jsonText->setText("Generated JSON: " + Wt::WString::fromUTF8(jsonString));
34 }
35 };

这个例子演示了如何在 Wt 应用中集成 rapidjson JSON 库,并使用 rapidjson 生成 JSON 字符串。

11.2.2 集成 JavaScript 第三方库 (Integration with JavaScript Third-party Libraries)

虽然 Wt 主要使用 C++ 进行开发,但在某些情况下,可能需要集成 JavaScript 第三方库,例如:

富文本编辑器 (Rich Text Editors): 例如 CKEditor, TinyMCE, Quill.js 等,提供更强大的富文本编辑功能。

图表库 (Chart Libraries): 例如 Chart.js, ECharts, Highcharts, D3.js 等,提供更丰富的图表类型和更精细的图表定制能力。

地图库 (Map Libraries): 例如 Leaflet, OpenLayers, Google Maps JavaScript API, Baidu Maps API 等,提供地图展示和地图交互功能。

UI 组件库 (UI Component Libraries): 例如 jQuery UI, Bootstrap JavaScript, Materialize CSS, Semantic UI 等,提供额外的 UI 组件或增强现有组件的功能和样式。

集成 JavaScript 第三方库的方法:

引入 JavaScript 库文件 (Include JavaScript Library Files)
将 JavaScript 库的 .js 文件放置在 Wt 应用的 public 目录下,并在 HTML 页面中引入这些文件。可以使用 Wt::WApplication::requireJavaScript() 方法在 Wt 应用中引入 JavaScript 文件。

创建 JavaScript 组件封装 (Create JavaScript Widget Wrapper)
为了在 C++ 代码中方便地操作 JavaScript 库,可以创建 C++ 组件类,作为 JavaScript 组件的封装器 (Wrapper)。在 C++ 组件类中,可以使用 Wt::WTemplate 组件嵌入 HTML 结构,并在 JavaScript 代码中初始化和操作 JavaScript 库。

C++ 和 JavaScript 代码交互 (C++ and JavaScript Code Interaction)
使用 Wt::WTemplate::bindWidget() 方法将 C++ 组件与 HTML 元素绑定。使用 Wt::WTemplate::javaScript() 方法执行 JavaScript 代码。使用 Wt::Signals 和 JavaScript 事件监听机制实现 C++ 和 JavaScript 代码之间的双向通信。

示例:集成 Chart.js 图表库 (Example: Integration with Chart.js Chart Library)

以下代码仅为示例,可能需要根据 Chart.js 版本和具体需求进行调整。

  1. 将 Chart.js 文件 (例如 chart.umd.js) 放入 public/js/ 目录。

  2. 创建 C++ Chart.js 组件封装类 ChartJSWidget:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WContainerWidget.h>
2 #include <Wt/WTemplate.h>
3 #include <Wt/WApplication.h>
4
5 class ChartJSWidget : public Wt::WContainerWidget
6 {
7 public:
8 ChartJSWidget(Wt::WContainerWidget *parent = nullptr) : Wt::WContainerWidget(parent)
9 {
10 // 引入 Chart.js JavaScript 文件
11 WApplication::instance()->requireJavaScript("js/chart.umd.js");
12
13 // 使用 WTemplate 嵌入 HTML 结构
14 WTemplate *template_ = setTemplateContent("<canvas id='myChart'></canvas>", Wt::TextFormat::XHTML);
15
16 // 初始化 Chart.js 图表的 JavaScript 代码
17 template_->javaScript() =
18 "var ctx = document.getElementById('myChart').getContext('2d');"
19 "var myChart = new Chart(ctx, {"
20 " type: 'bar'," // 默认图表类型为柱状图
21 " data: {"
22 " labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],"
23 " datasets: [{"
24 " label: '# of Votes',"
25 " data: [12, 19, 3, 5, 2, 3],"
26 " backgroundColor: ["
27 " 'rgba(255, 99, 132, 0.2)',"
28 " 'rgba(54, 162, 235, 0.2)',"
29 " 'rgba(255, 206, 86, 0.2)',"
30 " 'rgba(75, 192, 192, 0.2)',"
31 " 'rgba(153, 102, 255, 0.2)',"
32 " 'rgba(255, 159, 64, 0.2)'"
33 " ],"
34 " borderColor: ["
35 " 'rgba(255, 99, 132, 1)',"
36 " 'rgba(54, 162, 235, 1)',"
37 " 'rgba(255, 206, 86, 1)',"
38 " 'rgba(75, 192, 192, 1)',"
39 " 'rgba(153, 102, 255, 1)',"
40 " 'rgba(255, 159, 64, 1)'"
41 " ],"
42 " borderWidth: 1"
43 " }]"
44 " },"
45 " options: {"
46 " scales: {"
47 " y: {"
48 " beginAtZero: true"
49 " }"
50 " }"
51 " }"
52 "});";
53 }
54
55 void setChartType(const std::string& chartType) {
56 WTemplate *template_ = static_cast<WTemplate*>(layout()->widget(0));
57 template_->javaScript() +=
58 "myChart.config.type = '" + chartType + "'; myChart.update();";
59 }
60 };
  1. 在 Wt 应用中使用 ChartJSWidget:
1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <Wt/WApplication.h>
2 #include "ChartJSWidget.h" // 包含 ChartJSWidget 头文件
3 #include <Wt/WComboBox.h>
4 #include <Wt/WVBoxLayout.h>
5
6 class ChartJSExample : public Wt::WApplication
7 {
8 public:
9 ChartJSExample(const Wt::WEnvironment& env) : Wt::WApplication(env)
10 {
11 setTitle("Chart.js Integration Example");
12
13 WVBoxLayout *vbox = new WVBoxLayout();
14 ChartJSWidget *chartWidget = new ChartJSWidget();
15 WComboBox *chartTypeComboBox = new WComboBox();
16
17 vbox->addWidget(chartTypeComboBox);
18 vbox->addWidget(chartWidget);
19 root()->setLayout(vbox);
20
21 chartTypeComboBox->addItem("Bar Chart", "bar");
22 chartTypeComboBox->addItem("Line Chart", "line");
23 chartTypeComboBox->addItem("Pie Chart", "pie");
24 chartTypeComboBox->setCurrentIndex(0);
25
26 chartTypeComboBox->changed().connect([=]() {
27 chartWidget->setChartType(chartTypeComboBox->currentValue().toUTF8());
28 });
29 }
30 };

这个例子创建了一个 ChartJSWidget 组件,封装了 Chart.js 图表库。可以通过 C++ 代码控制 Chart.js 图表的类型。

11.3 Wt 的未来发展趋势 (Future Trends of Wt)

Wt 框架作为一个成熟且持续发展的 Web 开发框架,其未来发展趋势主要体现在以下几个方面:

持续的版本更新与功能增强 (Continuous Version Updates and Feature Enhancements)
Wt 团队会持续发布新版本,修复 Bug,提升性能,并添加新的功能和组件。关注 Wt 官方网站和更新日志,及时了解 Wt 的最新动态。

WebAssembly (Wasm) 支持 (WebAssembly Support)
WebAssembly 是一种新的 Web 标准,允许在浏览器中运行高性能的二进制代码。Wt 框架未来可能会加强 WebAssembly 支持,例如将 Wt 应用编译为 WebAssembly 模块,在客户端浏览器端执行部分业务逻辑,进一步提升性能和降低服务器负载。

渐进式 Web 应用 (Progressive Web Apps, PWAs) 支持 (PWA Support)
PWAs 是一种使用 Web 技术构建的,具有原生应用体验的 Web 应用。Wt 框架未来可能会加强 PWA 支持,例如 Service Worker 集成、离线缓存、推送通知等功能,使 Wt 应用能够更好地适应移动设备和离线环境。

云原生 (Cloud-Native) 与微服务 (Microservices) 支持 (Cloud-Native and Microservices Support)
云原生和微服务架构是现代 Web 应用开发的重要趋势。Wt 框架未来可能会加强云原生和微服务支持,例如更好的容器化 (Docker, Kubernetes) 支持、微服务架构集成、服务发现与注册、API 网关集成等,使 Wt 应用更易于部署和扩展在云环境中。

与其他 Web 技术融合 (Integration with Other Web Technologies)
Wt 框架会继续加强与其他 Web 技术的融合,例如与流行的 JavaScript 框架 (React, Vue, Angular) 的集成、与 Web Components 标准的兼容性、GraphQL API 支持等,使 Wt 应用能够更好地利用现有的 Web 技术生态系统。

社区生态建设 (Community Ecosystem Development)
Wt 社区的活跃度和生态系统的完善程度直接关系到 Wt 框架的长期发展。Wt 团队和社区会继续努力建设 Wt 社区生态,例如提供更完善的文档、示例、教程、工具和扩展库,吸引更多开发者加入 Wt 社区,共同推动 Wt 框架的发展。

通过关注 Wt 的未来发展趋势,并持续学习和实践,你将能够更好地掌握 Wt 框架,并利用 Wt 构建更先进、更强大的 Web 应用。 😊 接下来,我们将进入最后一个章节,附录:Wt 常用 API 速查手册。

12. chapter 12:附录:Wt 常用 API 速查手册

12.1 WApplication 类

Wt::WApplication 类是 Wt Web 应用的核心类,代表一个应用程序实例。

setTitle(const Wt::WString& title):设置应用标题(显示在浏览器标签页或窗口标题栏)。
root():获取应用的根组件 (Wt::WContainerWidget*),所有组件都必须添加到根组件或其子组件下。
setTheme(const std::string& themeName):设置应用主题。
setLocale(const Wt::WLocale& locale):设置应用的语言环境 (Locale)。
setPreferredLanguages(const std::vector<std::string>& languages):设置应用的首选语言列表。
setStyleSheet(const Wt::WString& css):设置全局 CSS 样式表。
requireJavaScript(const Wt::WString& js):引入 JavaScript 文件。
resolvePath(const std::string& path):解析内部路径,用于文件下载等场景。
environment():获取当前请求的环境信息 (Wt::WEnvironment&),例如会话信息、客户端信息等。
sessionId():获取当前会话 ID。
setCursor(Cursor cursor):设置鼠标光标样式。

12.2 组件类 (Wt::WWidget)

Wt::WWidget 类是所有 Wt 组件的基类,提供了组件的通用接口。

addWidget(std::unique_ptr<W> widget):添加子组件。
setLayout(std::unique_ptr<L> layout):设置组件的布局管理器。
setStyleClass(const std::string& styleClass):设置 CSS 类名。
setStyleProperty(const std::string& name, const std::string& value):设置内联 CSS 样式属性。
setDraggable(bool draggable):设置组件是否可拖拽。
setDropTarget(bool dropTarget):设置组件是否为拖放目标。
signalName().connect(std::bind(…)):连接信号到槽函数。 例如 clicked().connect(…) 连接点击信号。
id():获取组件的唯一 ID (字符串类型)。
setId(const std::string& id):设置组件的 ID。
setToolTip(const Wt::WString& text):设置鼠标悬停提示文本。
setHidden(bool hidden):设置组件是否隐藏。
isHidden() const:判断组件是否隐藏。
isEnabled() const:判断组件是否启用。
setDisabled(bool disabled):设置组件是否禁用。
parent():获取父组件 (Wt::WWidget*)。
clear():移除所有子组件。
layout():获取组件的布局管理器 (Wt::WLayout*)。
width() constheight() const:获取组件的宽度和高度 (Wt::WLength)。
resize(const Wt::WLength& width, const Wt::WLength& height)resize(const Wt::WSize& size):设置组件尺寸。
setMinimumSize(const Wt::WSize& size)setMaximumSize(const Wt::WSize& size):设置组件最小和最大尺寸。
focus():使组件获得焦点。
blur():移除组件焦点。

12.3 布局管理器类 (Layout Managers)

12.3.1 Wt::WBoxLayout (盒式布局)

addWidget(std::unique_ptr<W> widget, int stretch = 0, AlignmentFlag alignment = AlignmentFlag::None):添加组件,并可设置伸缩比例和对齐方式。
insertWidget(int index, std::unique_ptr<W> widget, int stretch = 0, AlignmentFlag alignment = AlignmentFlag::None):插入组件到指定索引位置。
setSpacing(int spacing):设置组件之间的间距 (像素)。
setContentsMargins(int left, int top, int right, int bottom):设置容器的内外边距。

12.3.1.1 Wt::WHBoxLayout (水平盒式布局)
12.3.1.2 Wt::WVBoxLayout (垂直盒式布局)

12.3.2 Wt::WGridLayout (网格布局)

addWidget(std::unique_ptr<W> widget, int row, int column, int rowSpan = 1, int columnSpan = 1, AlignmentFlag alignment = AlignmentFlag::None):添加组件到指定行列位置,并可设置跨行跨列和对齐方式。
addLayout(std::unique_ptr<L> layout, int row, int column, int rowSpan = 1, int columnSpan = 1, AlignmentFlag alignment = AlignmentFlag::None):添加布局到指定行列位置。
setColumnStretch(int column, int stretch):设置列的伸缩比例。
setRowStretch(int row, int stretch):设置行的伸缩比例。
setColumnSpacing(int column, int spacing):设置列间距。
setRowSpacing(int row, int spacing):设置行间距。

12.3.3 Wt::WStackedWidget (堆叠布局)

addWidget(std::unique_ptr<W> widget):添加组件到堆叠布局中。
setCurrentIndex(int index):设置当前显示的组件索引。
setCurrentWidget(WWidget *widget):设置当前显示的组件。

12.3.4 Wt::WBorderLayout (边框布局)

addWidget(std::unique_ptr<W> widget, Area area):添加组件到指定区域 (Area:North, South, West, East, Center)。

12.4 表单组件类 (Form Widgets)

12.4.1 Wt::WLineEdit (单行文本输入框)

setText(const Wt::WString& text):设置文本内容。
text() const:获取文本内容 (Wt::WString)。
setPlaceholderText(const Wt::WString& text):设置占位符文本。
setInputType(InputType type):设置输入类型 (例如 Text, Password, Number)。
setMaxLength(int length):设置最大输入长度。
setReadOnly(bool readOnly):设置是否只读。
textChanged():文本内容改变信号。
enterPressed():按下回车键信号。
setValidator(std::unique_ptr<V> validator):设置验证器 (Wt::WValidator)。

12.4.2 Wt::WPushButton (按钮)

setText(const Wt::WString& text):设置按钮文本。
clicked():按钮点击信号。
setDisabled(bool disabled):设置按钮是否禁用。

12.4.3 Wt::WCheckBox (复选框)

setText(const Wt::WString& text):设置复选框文本。
isChecked() const:获取选中状态 (bool)。
setChecked(bool checked):设置选中状态。
checkedChanged():选中状态改变信号。

12.4.4 Wt::WComboBox (下拉列表框)

addItem(const Wt::WString& text, const Wt::cpp17::any& data = Wt::cpp17::any()):添加列表项,可设置关联数据。
insertItem(int index, const Wt::WString& text, const Wt::cpp17::any& data = Wt::cpp17::any()):在指定索引位置插入列表项。
currentText() const:获取当前选中文本 (Wt::WString)。
currentIndex() const:获取当前选中项索引 (int)。
setCurrentIndex(int index):设置当前选中项索引。
setCurrentText(const Wt::WString& text):设置当前选中文本。
changed():选中项改变信号。

12.4.5 Wt::WSpinBox (数值微调器)

setValue(double value):设置数值。
value() const:获取数值 (double)。
setMinimum(double min)setMaximum(double max):设置数值范围。
setSingleStep(double step):设置步长。
valueChanged():数值改变信号。

12.4.6 Wt::WSlider (滑块)

setValue(double value):设置数值。
value() const:获取数值 (double)。
setMinimum(double min)setMaximum(double max):设置数值范围。
setSingleStep(double step):设置步长。
valueChanged():数值改变信号。

12.4.7 Wt::WFileUpload (文件上传)

isReady() const:判断是否有已选择的文件等待上传。
upload():开始文件上传 (手动触发上传时使用)。
uploaded():文件上传完成信号。
clientFileName() const:获取客户端文件名 (Wt::WString)。
contentType() const:获取文件 Content-Type (Wt::WString)。
spoolFile():获取文件输入流 (std::istream*)。
setMaximumSize(int maxSize):设置最大允许上传文件大小 (字节)。
setFilters(const Wt::WString& filters):设置允许上传的文件类型过滤器 (MIME 类型)。

12.5 数据模型类 (Data Models)

12.5.1 Wt::WAbstractItemModel (抽象条目模型)

rowCount(const Wt::WModelIndex& parent = WModelIndex()) const:获取行数。
columnCount(const Wt::WModelIndex& parent = WModelIndex()) const:获取列数。
data(const Wt::WModelIndex& index, ItemDataRole role = DisplayRole) const:获取指定索引的数据。
headerData(int section, Orientation orientation, ItemDataRole role = DisplayRole) const:获取表头数据。
flags(const Wt::WModelIndex& index) const:获取指定索引的标志 (Flags)。
setData(const Wt::WModelIndex& index, const Wt::WVariant& value, ItemDataRole role = EditRole):设置指定索引的数据 (需要子类实现)。

12.5.2 Wt::WStandardItemModel (标准条目模型)

setItem(int row, int column, std::unique_ptr<WStandardItem> item):设置指定行列的条目。
setHeaderData(int section, Orientation orientation, const WVariant& value, ItemDataRole role = DisplayRole):设置表头数据。
insertRow(int row, const WModelIndex& parent = WModelIndex()):插入行。
removeRow(int row, const WModelIndex& parent = WModelIndex()):移除行。
insertColumn(int column, const WModelIndex& parent = WModelIndex()):插入列。
removeColumn(int column, const WModelIndex& parent = WModelIndex()):移除列。

12.6 信号与槽 (Signals and Slots)

信号 (Signal):组件发出的事件通知。
槽 (Slot):用于响应信号的函数。

定义信号:在类定义中使用 Wt::Signal<ArgTypes...> 声明信号成员变量。 例如: Wt::Signal<> clicked; (无参数信号), Wt::Signal<int, Wt::WString> dataChanged; (带两个参数的信号)。

发射信号:在事件发生时,调用信号的 emit(Args...) 方法发射信号。例如:clicked.emit();, dataChanged.emit(newDataId, newDataValue);

连接信号到槽:使用信号的 connect(std::bind(…)) 方法将信号连接到槽函数。可以使用成员函数、自由函数或 Lambda 表达式作为槽函数。例如: button->clicked().connect(std::bind(&MyClass::onButtonClicked, this)); (连接到成员函数), button->clicked().connect([](){ /* Lambda 表达式槽函数 */ });

希望这份速查手册能帮助你快速查阅 Wt 常用 API。 😊 祝你 Wt 开发顺利!