C++无障碍化:如何让代码人人可用

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

为什么无障碍化不是“弱者”的专利,而是代码质量的体现

\n\n

当我们用C++编程时,我们习惯于考虑性能、内存和速度。但还有一个经常被忽略的方面——无障碍化。这不仅仅是来自Web开发领域的一个时髦术语。在C++中,应用的可访问性意味着让有障碍的人士(视力、听力、行动障碍)能够与其他人一样使用你的软件。

\n\n

想象一下:你创建了一个很棒的图形编辑器或控制台工具。但如果它无法被屏幕阅读器读取,或者无法通过键盘操作——你就切断了15-20%的用户。此外,无障碍化通常是法律要求(例如,在美国是Section 508,在欧洲是EN 301 549)。

\n\n

在本文中,我们将探讨如何在不牺牲性能的情况下,将a11y最佳实践(a11y是accessibility的数字缩写)融入C++项目。开始吧!

\n\n

从何入手:包容性代码的基本原则

\n\n

在深入代码之前,请记住三大支柱:

\n\n
    \n
  • 可感知性——信息应至少通过一种感官(视觉、听觉、触觉)可获取。
  • \n
  • 可操作性——界面应能通过键盘、语音或开关设备操作。
  • \n
  • 可理解性——元素行为可预测,错误信息清晰易懂。
  • \n
\n\n

在C++中,这一点尤为重要,因为我们经常处理底层API,语义很容易丢失。例如,通过OpenGL绘制一个按钮,你不会自动获得屏幕阅读器的支持——需要手动添加。

\n\n

C++ GUI中的语义化标记:Qt及其他

\n\n

在C++中添加无障碍化最常见的方法是使用Qt框架。它通过QAccessibleInterface提供了内置支持。但即使你使用纯WinAPI,也有IAccessible可用。

\n\n

示例:让按钮可访问

\n\n

假设你有一个通过QPainter绘制的自定义按钮。没有无障碍化,屏幕阅读器只会看到“矩形”。我们来修复它:

\n\n
#include <QAccessible>\n#include <QAccessibleWidget>\n#include <QPushButton>\n\nclass AccessibleButton : public QAccessibleWidget {\npublic:\n    explicit AccessibleButton(QWidget* widget)\n        : QAccessibleWidget(widget, QAccessible::Button) {}\n\n    QString text(QAccessible::Text t) const override {\n        if (t == QAccessible::Name) {\n            auto* btn = qobject_cast<QPushButton*>(widget());\n            return btn ? btn->text() : QString();\n        }\n        return QAccessibleWidget::text(t);\n    }\n};\n\n// 使用方式:\nQPushButton* btn = new QPushButton("提交");\nQAccessible::registerAccessibleFactory(\n    [](QObject* obj) -> QAccessibleInterface* {\n        if (auto* btn = qobject_cast<QPushButton*>(obj)) {\n            return new AccessibleButton(btn);\n        }\n        return nullptr;\n    }\n);
\n\n

现在屏幕阅读器会读出“提交,按钮”。神奇吗?不,这只是包容性代码。

\n\n

C++控制台应用中的无障碍化

\n\n

控制台看似过时,但许多工具、CI/CD流水线和服务器应用都在终端中运行。如何让它们可访问?

\n\n

为屏幕阅读器正确输出

\n\n

屏幕阅读器(例如NVDA、JAWS)逐行读取控制台。如果你用ASCII字符输出表格——那将是一场噩梦。最好使用结构化输出:

\n\n
#include <iostream>\n#include <string>\n#include <vector>\n\nstruct User {\n    std::string name;\n    int age;\n};\n\nvoid printAccessibleTable(const std::vector<User>& users) {\n    // 屏幕阅读器友好:先输出标题,再输出数据\n    std::cout << "用户表格:" << std::endl;\n    for (const auto&                

博客

书籍推荐