目录1. Label代码示例显示不同格式的文本代码示例显示图片代码示例文本对齐自动换行缩进边距代码示例设置伙伴2. LCD Number代码示例倒计时重写updateTime采用 sleep 实现倒计时采用新线程实现倒计时3. ProgressBar代码示例设置进度条按时间增长代码示例创建一个红色的进度条4. Calendar Widget代码示例获取选中的日期1. LabelQLabel 可以用来显示文本和图片。 核心属性如下属性说明textQLabel 中的文本。textFormat文本的格式。Qt::PlainText纯文本Qt::RichText富文本支持 HTML 标签Qt::MarkdownTextMarkdown 格式Qt::AutoText根据文本内容自动决定文本格式pixmapQLabel 内部包含的图片。scaledContents设为 true 表示内容自动拉伸填充设为 false 则不会自动拉伸。alignment对齐方式。可以设置水平和垂直方向如何对齐。wordWrap设为 true 内部的文本会自动换行设为 false 则内部文本不会自动换行。indent设置文本缩进。水平和垂直方向都生效。margin内部文本和边框之间的边距。不同于 indentmargin 是上下左右四个方向都同时有效而 indent 最多只是两个方向有效具体哪两个方向有效取决于 alignment。openExternalLinks是否允许打开一个外部的链接当 QLabel 文本内容包含 URL 的时候涉及到。buddy给 QLabel 关联一个“伙伴”这样点击 QLabel 时就能激活对应的伙伴。例如伙伴如果是一个 QCheckBox那么该 QCheckBox 就会被选中。代码示例显示不同格式的文本在界面上创建三个 QLabel尺寸放大一些objectName 分别为 labellabel_2label_3Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui-setupUi(this); ui-label-setTextFormat(Qt::PlainText); ui-label-setText(这是⼀段纯⽂本); ui-label_2-setTextFormat(Qt::RichText); ui-label_2-setText(b 这是⼀段富⽂本 /b); ui-label_3-setTextFormat(Qt::MarkdownText); ui-label_3-setText(## 这是⼀段 markdown ⽂本); }代码示例显示图片虽然 QPushButton 也可以通过设置图标的方式设置图片但是并非是一个好的选择。更多的时候还是希望通过 QLabel 来作为一个更单纯的显示图片的方式在界面上创建一个 QLabelobjectName 为 label。创建 resource.qrc 文件并把图片导入到 qrc 中。// 设置 label ⼤⼩和窗⼝⼀样⼤ ui-label-setGeometry(0, 0, 800, 600); QPixmap pixmap(:/huaji.png); ui-label-setPixmap(pixmap);这个图片本身的尺寸是 480 * 480并没有把 QLabel 填充满。修改代码设置 scaledContents 属性。// 设置内容伸缩 ui-label-setScaledContents(true);此时如果拖动窗口大小可以看到图片并不会随着窗口大小的改变而同步变化。为了解决这个问题可以在 Widget 中重写 resizeEvent 函数。resizeEvent.会在窗口大小发生改变时被自动调用.// 在 Widget 中重写 resizeEvent. 这个函数会在窗⼝⼤⼩发⽣改变时被⾃动调⽤. void Widget::resizeEvent(QResizeEvent *event) { // 可以直接通过 this-width() 和 this-height() 设置 label 新的尺⼨, 也可以通过 event 参数拿到新的尺⼨. // ui-label-setGeometry(0, 0, this-width(), this-height()); ui-label-setGeometry(0, 0, event-size().width(), event-size().height()); qDebug() event-size(); }注意此处的 resizeEvent 函数我们没有手动调用但是能在窗口大小变化时被自动调用。这个过程就是依赖 C 中的多态来实现的。Qt 框架内部管理着 QWidget 对象表示咱们的窗口。在窗口大小发生改变时Qt 就会自动调用 resizeEvent 函数。但是由于实际上这个表示窗口的并非是 QWidget而是 QWidget 的子类也就是咱们自己写的 Widget。此时虽然是通过父类调用函数但是实际上执行的是子类的函数也就是我们重写后的 resizeEvent。此处属于是多态机制的一种经典用法。通过上述过程就可以把自定义的代码插入到框架内部执行。相当于“注册回调函数”。代码示例文本对齐自动换行缩进边距创建四个 labelobjectName 分别是 label 到 label_4并且在 QFrame 中设置 frameShape 为 Box设置边框之后看起来会更清晰一些。QFrame 是 QLabel 的父类其中 frameShape 属性用来设置边框性质。QFrame::Box矩形边框QFrame::Panel带有可点击区域的面板边框QFrame::WinPanelWindows风格的边框QFrame::HLine水平线边框QFrame::VLine垂直线边框QFrame::StyledPanel带有可点击区域的面板边框但样式取决于窗口主题编写 widget.cpp给这四个 label 设置属性。Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui-setupUi(this); // 设置⽂字居中对⻬ ui-label-setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); ui-label-setText(垂直⽔平居中的⽂本); // 设置⾃动换⾏ ui-label_2-setAlignment(Qt::AlignTop | Qt::AlignLeft); ui-label_2-setWordWrap(true); ui-label_2-setText(这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本); // 设置⾸⾏缩进 ui-label_3-setAlignment(Qt::AlignTop | Qt::AlignLeft); ui-label_3-setIndent(20); ui-label_3-setText(这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本); // 设置边距 ui-label_4-setAlignment(Qt::AlignTop | Qt::AlignLeft); ui-label_4-setMargin(20); ui-label_4-setText(这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本这是⼀个很⻓的⽂本); }运行程序可以看到如下效果第一个 label 垂直水平居中第二个 label 设置了 wordWrap能够自动换行第三个 label 设置了 Indent左侧和上方和边框有间距。右侧则没有。第四个 label 设置了 margin四个方向均有间距图上仅体现出三个方向下方看不出来。代码示例设置伙伴创建两个 label 和两个 radioButton。objectName 分别为 labellabel_2radioButtonradioButton_2。此处把 label 中的文本设置为“快捷键 A”这样的形式。其中 后面跟着的字符就是快捷键可以通过 alt A 的方式来触发该快捷键。但是注意这里的快捷键和 QPushButton 的不同。需要搭配 alt 和单个字母的方式才能触发编写 widget.cpp设置 buddy 属性。 当然这里也可以使用 Qt Designer 直接设置。Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui-setupUi(this); // 设置 label 的伙伴 widget ui-label-setBuddy(ui-radioButton); ui-label_2-setBuddy(ui-radioButton_2); }运行程序可以看到按下快捷键 alt a 或者 alt b即可选中对应的选项。2. LCD NumberQLCDNumber 是一个专门用来显示数字的控件。类似于“老式计算器”的效果。QLCDNumber 的核心属性如下属性说明intValueQLCDNumber 显示的数字值int。valueQLCDNumber 显示的数字值double和 intValue 是联动的。例如给 value 设为 1.5intValue 的值就是 2。另外设置 value 和 intValue 的方法名字为 display而不是 setValue 或者 setIntValue。digitCount显示几位数字。mode数字显示形式。QLCDNumber::Dec十进制模式显示常规的十进制数字。QLCDNumber::Hex十六进制模式以十六进制格式显示数字。QLCDNumber::Bin二进制模式以二进制格式显示数字。QLCDNumber::Oct八进制模式以八进制格式显示数字。只有十进制的时候才能显示小数点后的内容。segmentStyle设置显示风格。QLCDNumber::Flat平面的显示风格数字呈现在一个平坦的表面上。QLCDNumber::Outline轮廓显示风格数字具有清晰的轮廓和阴影效果。QLCDNumber::Filled填充显示风格数字被填充颜色并与背景区分开。smallDecimalPoint设置比较小的小数点。代码示例倒计时重写updateTime在界面上创建一个 QLCDNumber初始值设为 10。objectName 为 lcdNumber。修改 widget.h 代码创建一个 QTimer 成员和一个 updateTime 函数。QTimer* timer; void updateTime();修改 widget.cpp在构造函数中初始化 QTimerQTimer 表示定时器。通过 start 方法启动定时器之后就会每隔一定周期触发一次 QTimer::timeout 信号。使用 connect 把 QTimer::timeout 信号和 Widget::updateTime 连接起来意味着每次触发 QTimer::timeout 都会执行 Widget::updateTime。Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui-setupUi(this); // 创建 QTimer 实例 timer new QTimer(this); // 连接信号槽. QTimer 会每隔⼀定的时间触发⼀个 timeout 信号. 现在把 timeout 信号和 updateTime 连接起来. // 此时意味着每次触发 timeout 信号都会伴随 updateTime 函数的执⾏. connect(timer, QTimer::timeout, this, Widget::updateTime); // 启动 QTimer, 并且规定每隔 1000ms 触发⼀次 timeout 信号. timer-start(1000); }修改 widget.cpp实现 updateTime通过 intValue 获取到 QLCDNumber 内部的数值。如果 value 的值归 0 了就停止 QTimer。接下来 QTimer 也就不会触发 timeout 信号了。void Widget::updateTime() { qDebug() updateTime; int value ui-lcdNumber-intValue(); if (value 0) { // 如果时间到, 停⽌定时器. timer-stop(); return; } ui-lcdNumber-display(value - 1); }采用 sleep 实现倒计时针对上述代码存在两个问题上述代码如果直接在 Widget 构造函数中通过一个循环 sleep 的方式是否可以呢 代码形如Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui-setupUi(this); int value ui-lcdNumber-intValue(); while (true) { std::this_thread::sleep_for(std::chrono::seconds(1)); if (value 0) { break; } ui-lcdNumber-display(value - 1); } }显然这个代码是不行的。循环会使 Widget 的构造函数无法执行完毕此时界面是不能正确构造和显示的采用新线程实现倒计时上述代码如果是在 Widget 构造函数中另起一个线程在新线程中完成循环 sleep 是否可以呢 代码形如Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui-setupUi(this); std::thread t([this]() { int value this-ui-lcdNumber-intValue(); while (true) { std::this_thread::sleep_for(std::chrono::seconds(1)); if (value 0) { break; } this-ui-lcdNumber-display(value - 1); } }); }这个代码同样是不行的。Qt 中规定任何对于 GUI 上内容的操作必须在主线程中完成。像 Widget 构造函数以及 connect 连接的 slot 函数都是在主线程中调用的。而我们自己创建的线程则不是。当我们自己的线程中尝试对界面元素进行修改时Qt 程序往往会直接崩溃。这样的约定主要是因为 GUI 中的状态往往是牵一发而动全身的修改一个地方就需要同步的对其他内容进行调整。比如调整了某个元素的尺寸就可能影响到内部的文字位置或者其他元素的位置。这里一连串的修改都是需要按照一定的顺序来完成的。由于多线程执行的顺序无法保障因此 Qt 从根本上禁止了其他线程修改 GUI 状态避免后续的一系列问题。综上所述使用定时器是实现上述功能的最合理方案。 后续如果我们也有类似的需要“周期性修改界面状态”的需求也需要优先考虑使用定时器。3. ProgressBar使用 QProgressBar 表示一个进度条。注意不要把 ProgressBar 拼写成 ProcessBarQProgressBar 的核心属性如下属性说明minimum进度条最小值。maximum进度条最大值。value进度条当前值。alignment文本在进度条中的对齐方式。Qt::AlignLeft左对齐Qt::AlignRight右对齐Qt::AlignCenter居中对齐Qt::AlignJustify两端对齐textVisible进度条的数字是否可见。orientation进度条的方向是水平还是垂直。invertAppearance是否是朝反方向增长进度。textDirection文本的朝向。format展示的数字格式。%p表示进度的百分比0-100%v表示进度的数值0-100%m表示剩余时间以毫秒为单位%t表示总时间以毫秒为单位代码示例设置进度条按时间增长在界面上创建进度条objectName 为 progressBar。其中最小值设为 0最大值设为 100。当前值设为 0。修改 widget.h创建 QTimer 和 updateProgressBar 函数。QTimer* timer; void updateProgressBar ();修改 widget.cpp初始化 QTimer此处设置 100ms 触发一次 timeout 信号。也就是一秒钟触发 10 次。Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui-setupUi(this); timer new QTimer(this); connect(timer, QTimer::timeout, this, Widget::updateProgressBar); timer-start(100); }修改 widget.cpp实现 updateProgressBar。void Widget::updateProgressBar() { int value ui-progressBar-value(); if (value 100) { timer-stop(); return; } ui-progressBar-setValue(value 1); }在实际开发中进度条的取值往往是根据当前任务的实际进度来进行设置的。比如需要读取一个很大的文件就可以获取文件的总的大小和当前读取完毕的大小来设置进度条的比例。由于上面我们介绍了 Qt 禁止在其他线程修改界面因此进度条的更新往往也是需要搭配定时器来完成的。通过定时器周期触发信号主线程调用对应的 slot 函数。再在 slot 函数中对当前的任务进度进行计算并更新进度条的界面效果。代码示例创建一个红色的进度条上述的进度条使用绿色表示的但是考虑到有些人可能不喜欢绿色因此我们改成一个红色的进度条。不要忘了QProgressBar 同样也是 QWidget 的子类因此我们可以使用 styleSheet 通过样式来修改进度条的颜色。在 Qt Designer 右侧的属性编辑器中找到 QWidget 的 styleSheet 属性。 编辑如下内容其中的 chunk 是选中进度条中的每个“块”。使用 QProgressBar::text 则可以选中文本。QProgressBar::chunk {background-color: #FF0000;}同时把 QProgressBar 的 alignment 属性设置为垂直水平居中。如果不设置 alignment进度条中的数字会跑到左上角。这个怀疑是 Qt 本身的 bug暂时只能先使用 alignment 来手动调整下。通过上述方式也可以修改文字的颜色字体大小等样式。4. Calendar WidgetQCalendarWidget 表示一个“日历”形如QCalendarWidget 的核心属性如下属性说明selectDate当前选中的日期。minimumDate最小日期。maximumDate最大日期。firstDayOfWeek每周的第一天也就是日历的第一列是周几。gridVisible是否显示表格的边框。selectionMode是否允许选择日期。navigationBarVisible日历上方标题是否显示。horizontalHeaderFormat日历上方标题显示的日期格式。verticalHeaderFormat日历第一列显示的内容格式。dateEditEnabled是否允许日期被编辑。QCalendarWidget 的核心信号如下信号说明selectionChanged(const QDate)当选中的日期发生改变时发出。activated(const QDate)当双击一个有效的日期或者按下回车键时发出形参是一个 QDate 类型保存了选中的日期。currentPageChanged(int, int)当年份月份改变时发出形参表示改变后的新年份和月份。代码示例获取选中的日期在界面上创建一个 QCalendarWidget 和一个 labelobjectName 为 calendarWidgetlabel。给 QCalendarWidget 添加 slot 函数。void Widget::on_calendarWidget_selectionChanged() { QDate date ui-calendarWidget-selectedDate(); qDebug() date; ui-label-setText(date.toString()); }执行程序可以看到当选择不同的日期时label 中的内容就会随之改变。