本篇专门扯一下有关 QCheckBox 组件的一个问题。老周不水字数,直接上程序,你看了就明白。
#include <QApplication> #include <QWidget> #include <QPushButton> #include <QCheckBox> #include <QVBoxLayout> #include <QIcon> int main(int argc, char **argv) { QApplication app(argc, argv); // 最平庸的窗口 QWidget window; window.setWindowTitle("看看这是?"); window.setGeometry(/*坐标*/600, 450, /*大小*/280, 170); // 布局 QVBoxLayout *layout = new QVBoxLayout; window.setLayout(layout); // 控件列表 QCheckBox *cb = new QCheckBox(&window); cb -> setText("看,左边的图标不会变"); layout -> addWidget(cb); QPushButton *btn = new QPushButton(&window); btn->setText("看,左边的图标会变"); // 让按钮支持check操作 btn->setCheckable(true); layout->addWidget(btn); // 图标 QIcon icon; // 第一个图,当checked的时候显示 icon.addFile("1.png", QSize(), QIcon::Normal, QIcon::On); // 第二个图,当unchecked的时候显示 icon.addFile("2.png", QSize(), QIcon::Normal, QIcon::Off); // 应用图标 cb->setIcon(icon); btn->setIcon(icon); // 显示窗口 window.show(); return QApplication::exec(); }
QCheckBox、QRadioButton、QPushButton 都是 QAbstractButton 的子类,所以这几个家伙都归属于按钮组件。在 QAbstractButton 类中已定义有 checkable 属性,表示按钮是否支持 check 操作。这种按钮就类似于现实世界中的自锁按钮——按一下【开】,再按一下【关】。而普通的按钮是无状态记忆的,按下去+弹起来为一个周期,称为 Click。
由于是派生关系,所以 QCheckBox 和 QPushButton 类都会继承 check 功能的支持,只是 QCheckBox 默认是打开这项功能的。QPushButton 类默认不能 check,所以得调用 setCheckable 方法手动打开功能。
btn->setCheckable(true);
下面重点扯扯 QIcon 这货,看名识义,你能猜到它表示的是图标。QIcon 类可以根据不同状态添加多个图标。老周这个例子是添加了两个 PNG 图标。懒得生成什么鬼 Qrc 资源了,直接把图片文件复制到程序可执行文件所在的目录就完事了。比如,build/Debug,只要和可执行文件在同一个目录就行,方便相对路径引用。
QIcon icon; // 第一个图,当checked的时候显示 icon.addFile("1.png", QSize(), QIcon::Normal, QIcon::On); // 第二个图,当unchecked的时候显示 icon.addFile("2.png", QSize(), QIcon::Normal, QIcon::Off);
这里涉及到 QIcon 类定义的两枚举:
A、Mode:
1)Normal —— 组件的正常状态;
2)Disbaled —— 被禁用,比如按钮不能点击;
3)Active —— 其实和 Normal 差不多,只是多了一条:正在与用户交互。比如活动窗口,获得焦点的按钮,获得焦点的输入框等;
4)Selected —— 这个有些奇葩,一般在子项对象起作用。比如,ListView 视图的子项被选中时。
B、State:
1)On —— 状态“开”。比如 CheckBox 处于 checked 状态;
2)Off —— 状态“关”。如 CheckBox 的 Unchecked 状态。
回到咱们的代码。这两个图标的区别就在 State 的值不同,当 On 时显示 1.png;当状态为 Off 时显示 2.png。
icon.addFile("1.png", QSize(), QIcon::Normal, QIcon::On); icon.addFile("2.png", QSize(), QIcon::Normal, QIcon::Off);
addFile 的第一个参数是文件路径,第二个参数指定图标的大小,这里用 QSize 类的默认构造函数,即宽和高都是 -1,这样图标会根据样式获取默认大小。
运行程序,咱们测试下。如下图所示,这是初始状态,上下两个组件都没有 check。
然后,咱们依次点击它们,让这两个组件都处于 checked 状态。
咱们看到:QPushButton 切换状态后图标也跟着变了,但是 QCheckBox 一点动静都没有。这 NM 是怎么回事?
原来,QPushButton 的默认样式中,在获取图标时,会根据 checked 状态来提取 On 或 Off 相关的图标。
case CE_PushButtonLabel: if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(opt)) { …… if (!button->icon.isNull()) { //Center both icon and text QIcon::Mode mode = button->state & State_Enabled ? QIcon::Normal : QIcon::Disabled; if (mode == QIcon::Normal && button->state & State_HasFocus) mode = QIcon::Active; QIcon::State state = QIcon::Off; if (button->state & State_On) state = QIcon::On; …… break;
再看看 QCheckBox 的样式,发现这厮在获取图标时是不考虑 On 或 Off 状态的。
case CE_RadioButtonLabel: case CE_CheckBoxLabel: …… if (!btn->icon.isNull()) { pix = btn->icon.pixmap(btn-