Qt常用控件(二)

一、QPushButton

使用 QPushButton 来表示按钮控件,这是我们目前最熟悉的控件之一。
QPushButton 继承自 QAbstractButton 类,后者是一个抽象类,作为所有按钮控件的基类。

在 Qt Designer 中也能够看到这里的继承关系

QAbstractButton 中, 和 QPushButton 相关性较大的属性

属性 说明
text 按钮中的文本
icon 按钮中的图标
iconSize 按钮中图标的尺寸
shortCut 按钮对应的快捷键
autoRepeat 按钮是否支持重复触发。当鼠标左键保持按下状态时:若设置为true,将持续触发点击事件(类似游戏手柄的"连发"模式)若设置为false,则需释放并再次点击才能触发新事件
autoRepeatDelay 重复触发的延时时间,按住按钮多久之后, 开始重复触发
autoRepeatInterval 重复触发的周期

代码样例:

先创建一个按钮:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

#include <QIcon>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //创建图标
    QIcon icon(":/dog.png");
    //设置图标
    ui->pushButton_target->setIcon(icon);
    //设置图片大小
    ui->pushButton_target->setIconSize(QSize(50, 50));
}

Widget::~Widget()
{
    delete ui;
}

运行代码:

我们再来一个升级版的样例(带有快捷键的按钮):

首先我们再创建四个按钮

再向resource.qrc中导入四张图片

注意导入的图片多的时候,可以创建一个新目录将图片统一放到一个文件夹中

此时就意味着后续访问这些图片就需要再路径中带上image这一级目录的名字。

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //设置图标
    ui->pushButton_target->setIcon(QIcon(":/image/dog.png"));
    ui->pushButton_target->setIconSize(QSize(80, 80));
    ui->pushButton_up->setIcon(QIcon(":/image/up.png"));
    ui->pushButton_down->setIcon(QIcon(":/image/down.png"));
    ui->pushButton_left->setIcon(QIcon(":/image/left.png"));
    ui->pushButton_right->setIcon(QIcon(":/image/right.png"));

    //设置快捷键
    ui->pushButton_up->setShortcut(QKeySequence("w"));
    ui->pushButton_down->setShortcut(QKeySequence("s"));
    ui->pushButton_left->setShortcut(QKeySequence("a"));
    ui->pushButton_right->setShortcut(QKeySequence("d"));
}

Widget::~Widget()
{
    delete ui;
}


void Widget::on_pushButton_up_clicked()
{
    const QRect& rect = ui->pushButton_target->geometry();
    ui->pushButton_target->setGeometry(rect.x(), rect.y() - 5, rect.width(),
    rect.height());
    qDebug() << "up";
}

void Widget::on_pushButton_down_clicked()
{
    const QRect& rect = ui->pushButton_target->geometry();
    ui->pushButton_target->setGeometry(rect.x(), rect.y() + 5, rect.width(),
    rect.height());
    qDebug() << "down";
}

void Widget::on_pushButton_left_clicked()
{
    const QRect& rect = ui->pushButton_target->geometry();
    ui->pushButton_target->setGeometry(rect.x() - 5, rect.y(), rect.width(),
    rect.height());
    qDebug() << "left";
}

void Widget::on_pushButton_right_clicked()
{
    const QRect& rect = ui->pushButton_target->geometry();
    ui->pushButton_target->setGeometry(rect.x() + 5, rect.y(), rect.width(),
    rect.height());
    qDebug() << "right";
}

代码运行:

但是我们发现只有键盘一直按着,才可以实现连发,鼠标长按却不能实现连发,我们可以加这样一个代码实现鼠标的连发。

cpp 复制代码
//开启重复触发
ui->pushButton_up->setAutoRepeat(true);
ui->pushButton_down->setAutoRepeat(true);
ui->pushButton_left->setAutoRepeat(true);
ui->pushButton_right->setAutoRepeat(true);

二、Radio Button

QRadioButton 是单选按钮,可以让我们在多个选项中选择⼀个

QAbstractButton 中和 QRadioButton 关系较大的属性

属性 说明
checkable 是否能选中
checked 是否已经被选中,checkable 是 checked 的前提条件
autoExclusive 是否排他,选中⼀个按钮之后是否会取消其他按钮的选中,对于 QRadioButton 来说默认就是排他的

代码样例(选择性别):

首先,先创建一个label和三个按钮

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}


void Widget::on_radioButton_male_clicked()
{
    ui->label->setText("你选择的性别为:男");
}

void Widget::on_radioButton_female_clicked()
{
    ui->label->setText("你选择的性别为:女");
}

void Widget::on_radioButton_order_clicked()
{
    ui->label->setText("你选择的性别为:其他");
}

运行代码,我们会发现随着选择的不同,label会随着选择发生变化

我们还可以这样,我们发现在启动的时候没有默认选择,我们可以加上一个默认选择的代码:

同样的我们也可以禁用按钮,举个例子我们将"其他"这个选项禁用

这里我们就发现了一个问题,这个其他的选项虽然点不下去,但是label中还是现实了其他,这里我们就发现setCheckable只是能够让按钮不被选中,但是依旧可以响应点击事件。所以我们可以换其他的,具体如下:

代码示例(click, press, release, toggled 的区别):

在界面上创建四个单选按钮

cpp 复制代码
void Widget::on_radioButton_clicked()
{
	qDebug() << "clicked";
}
void Widget::on_radioButton_2_pressed()
{
	qDebug() << "pressed";
}
void Widget::on_radioButton_3_released()
{
	qDebug() << "released";
}
void Widget::on_radioButton_4_toggled(bool checked)
{
	//状态发生改变就会触发这个信号
 	if (checked) {
 		qDebug() << "toggled checked true";
	} else {
 		qDebug() << "toggled checked false";
 	}
}

运行程序后可以观察到:

  • clicked 事件在鼠标按下并释放后触发
  • pressed 事件在鼠标按下时触发
  • released 事件在鼠标释放时触发
  • toggled 事件在 checked 属性发生变化时触发

综合来看,toggled 是最适合 QRadioButton 的事件类型。

代码样例(单选框分组):

界面上创建6个单选框,模拟麦当劳点餐

如上我们运行后发现,每个按钮之间会有排它的现象,那我们怎么解决这个问题呢?

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

#include <QButtonGroup>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //使用QButtonGroup,创建三个组
    QButtonGroup* group1 = new QButtonGroup(this);
    QButtonGroup* group2 = new QButtonGroup(this);
    QButtonGroup* group3 = new QButtonGroup(this);

    //把属于一个组的选项,分别归类
    group1->addButton(ui->radioButton);
    group1->addButton(ui->radioButton_2);
    group1->addButton(ui->radioButton_3);

    group2->addButton(ui->radioButton_4);
    group2->addButton(ui->radioButton_5);
    group2->addButton(ui->radioButton_6);

    group3->addButton(ui->radioButton_7);
    group3->addButton(ui->radioButton_8);
    group3->addButton(ui->radioButton_9);
}

Widget::~Widget()
{
    delete ui;
}

加入如上的代码,再运行:

三、Check Box

QCheckBox 是用于实现多选的复选框控件。与QCheckBox 相关的主要属性包括 checkable 和checked,这两个属性均继承自 QAbstractButton 基类。

代码示例:

创建三个复选按钮和一个普通按钮

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}


void Widget::on_pushButton_clicked()
{
    QString result;
    if(ui->checkBox_eat->isChecked())
    {
        result += ui->checkBox_eat->text();
        result += " ";
    }
    if(ui->checkBox_sleep->isChecked())
    {
        result += ui->checkBox_sleep->text();
        result += " ";
    }
    if(ui->checkBox_play->isChecked())
    {
        result += ui->checkBox_play->text();
        result += " ";
    }
    qDebug() << "选中的内容: " << result;
}

运行结果:

四、QLabel

QLabel 可以用来显示文本和图片

属性 说明
text QLabel 中的文本
textFormat 文本的格式:Qt::PlainText 纯文本,Qt::RichText 富文本(支持 html 标签),Qt::MarkdownText markdown 格式,Qt::AutoText 根据文本内容自动决定文本格式
pixmap QLabel 内部包含的图片
scaledContents 设为 true 表示内容自动拉伸填充 QLabel,设为 false 则不会自动拉伸
alignment 对齐方式,可以设置水平和垂直方向如何对齐
wordWrap 设为 true 内部的文本会自动换行,设为 false 则内部文本不会自动换行
indent 设置文本缩进. 水平和垂直方向都生效
margin 内部文本和边框之间的边距,不同于于 indent, 但是是上下左右四个方向都同时有效,而indent 最多只是两个方向有效(具体哪两个方向有效取决于 alignment
openExternalLinks 是否允许打开⼀个外部的链接(当 QLabel 文本内容包含 url 的时候涉及到)
buddy 给 QLabel 关联⼀个 "伙伴" , 这样点击 QLabel 时就能激活对应的伙伴,例如伙伴如果是⼀个 QCheckBox, 那么该 QCheckBox 就会被选中

代码示例:

创建三个QLable

cpp 复制代码
Widget::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 ⽂本");
}

运行结果:

显示图片

创建一个resource.qrc文件,将图片载入

修改 widget.cpp, 给QLabel 设置图片

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //设置label,让图片铺满窗口
    QRect windowRect = this->geometry();
    ui->label->setGeometry(0, 0, windowRect.width(), windowRect.height());

    QPixmap pixmap(":/mouse.jpg");
    ui->label->setPixmap(pixmap);
}

Widget::~Widget()
{
    delete ui;
}

运行结果:

看到上述结果(图片铺满了整个窗口),但是如果我们拖动窗口:

是不是发现了问题,其实上面的代码中是在构造函数里,进行这样的尺寸设置,这个设置相当于是"一次性的",一旦程序运行起来之后,QLabel的尺寸就固定下来了,窗口发生变化,此时QLabel是不会变化的。

如果我们想图片随着窗口的变大变大的话该怎么实现呢?这就是后续事件的内容了?用户的操作其实会对应一些信号,Qt中,表示用户的操作,有两类概念,一个是信号,另一个是事件。当用户拖拽修改窗户的大小的时候,就会触resize事件,想risize这样的事件,是连续变化的。把窗户尺寸从A拖拽到B这个过程,就会触发一系列的resizeEvent。此时就可以借助resizeEvent来完成上述的功能。做法是:可以方Widget窗口类重写父类QWidget的resizeEvent虚函数。在鼠标拖拽窗口尺寸的过程中这个函数就会被反复的调用执行。

所以修改上述代码:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

#include <QtDebug>
#include <QResizeEvent>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //设置label,让图片铺满窗口
    QRect windowRect = this->geometry();
    ui->label->setGeometry(0, 0, windowRect.width(), windowRect.height());

    QPixmap pixmap(":/mouse.jpg");
    ui->label->setPixmap(pixmap);
    //启动自动拉伸
    ui->label->setScaledContents(true);
}

Widget::~Widget()
{
    delete ui;
}
//此时的形参event非常有用,这里就包含了触发这个resize事件这一时刻,窗口的尺寸的数值
void Widget::resizeEvent(QResizeEvent *event)
{
    qDebug() << event->size();
    ui->label->setGeometry(0, 0, event->size().width(), event->size().height());
}

运行结果:

代码示例(文本对齐, 自动换行, 缩进, 边距)

创建四个 label, objectName 分别是 label 到 label_4

并且在 QFrame 中设置 frameShape 为 Box (设置边框之后看起来会更清晰一些)

QFrame 是 QLabel 的父类. 其中 frameShape 属性用来设置边框性质

  • QFrame::Box :矩形边框
  • QFrame::Panel :带有可点击区域的面板边框
  • QFrame::WinPanel :Windows风格的边框
  • QFrame::HLine :水平线边框
  • QFrame::VLine :垂直线边框
  • QFrame::StyledPanel :带有可点击区域的面板边框,但样式取决于窗口主题
cpp 复制代码
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 和 两个 radioButton.

objectName 分别问 label , label_2 , radioButton , radioButton_2

将 label 文本设置为"快捷键 &A"格式,其中 & 后的字符即为快捷键。

使用时需通过 alt + A 组合键触发,但需注意:该快捷键机制与 QPushButton 不同,必须配合 alt 键和单个字母才能生效

cpp 复制代码
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, 即可选中对应的选项

五、QLCDNumber

QLCDNumer 是⼀个专门用来显示数字的控件. 类似于 "老式计算器" 的效果

属性 说明
intValue QLCDNumber 显示的数字值(int)
value QLCDNumber 显示的数字值(double),和 intValue 是联动的,例如给 value 设为 1.5, intValue 的值就是 2,另外, 设置 value 和 intValue 的方法名字为 display , 而不是 setValue 或者 setIntValue
digitCount 显示几位数字
mode 数字显示形式:QLCDNumber::Dec :十进制模式,显示常规的十进制数字;QLCDNumber::Hex :十六进制模式,以十六进制格式显示数字;QLCDNumber::Bin :二进制模式,以二进制格式显示数字;QLCDNumber::Oct :八进制模式,以八进制格式显示数字
segmentStyle 设置显示风格:QLCDNumber::Flat :平面的显示风格,数字呈现在一个平坦的表面上;QLCDNumber::Outline :轮廓显示风格,数字具有清晰的轮廓和阴影效果;QLCDNumber::Filled :填充显示风格,数字被填充颜色并与背景区分开
smallDecimalPoint 设置比较小的小数点

代码样例(倒计时):

在界面上创建一个lcdNumber

在widget.h中添加一个QTimer成员和一个updayTime函数

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

#include <QTimer>
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //创建QTimer实例
    timer = new QTimer(this);
    //连接信号槽,QTimer每隔一段时间就会触发一个timeout信号,把timeout信号和创建的updayTime联系起来
    //此时意味着每次触发timeout信号,就会伴随着updayTime函数执行
    connect(timer, &QTimer::timeout, this, &Widget::updayTime);
    //启动QTimer,并且规定没1000ms触发一次信号
    timer->start(1000);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::updayTime()
{
    //通过inValue获取LCDnumber内部的值
    int value = ui->lcdNumber->intValue();
    qDebug() << "lcdNumber " << value;
    if(value <= 0)
    {
        timer->stop();
        return ;
    }
    ui->lcdNumber->display(value - 1);
}

运行代码:

针对上面的代码还有两个问题:

1)上述代码能不能在Widget的构造函数中,直接循环+sleep的方式呢?

将代码如下修改:

cpp 复制代码
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);
 	}
}

运行结果:

2)能不能另起一个新的线程完成循环+sleep呢?

cpp 复制代码
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 程序通常会发生崩溃。

六、ProgressBar

使用 QProgressBar 表示一个进度条

属性 说明
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 :表示总时间(以毫秒为单位)

代码样例(设置进度条按时间增长):

在界面上创建一个进度条

在widget.h中创建QTimer和updayprogressBar函数

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

#include <QTimer>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //初始化QTimer
    timer = new QTimer(this);
    //连接
    connect(timer, &QTimer::timeout, this, &Widget::updayprogressBar);
    timer->start(100);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::updayprogressBar()
{
    int value = ui->progressBar->value();
    if(value >= 100)
    {
        timer->stop();
        return ;
    }
    ui->progressBar->setValue(value + 1);
}

运行结果:

如果你不喜欢上述的绿色,还可以这样更换颜色:

运行结果:

七、Calendar Widget

QCalendarWidget 表示⼀个 "日历" , 形如

属性 说明
selectDate 当前选中的日期
minimumDate 最小日期
maximumDate 最大日期
firstDayOfWeek 每周的第⼀天(也就是日历的第⼀列) 是周几
gridVisible 是否显示表格的边框
selectionMode 是否允许选择日期
navigationBarVisible 日历上方标题是否显示
horizontalHeaderFormat 日历上方标题显示的日期格式
verticalHeaderFormat 日历第⼀列显示的内容格式
dateEditEnabled 是否允许日期被编辑

重要的信号

信号 说明
selectionChanged(const QDate&) 当选中的日期发生改变时发出
activated(const QDate&) 当双击⼀个有效的日期或者按下回车键时发出,形参是⼀个QDate类型,保存了选中的日期
currentPageChanged(int, int) 当年份月份改变时发出,形参表示改变后的新年份和月份

代码样例(获取选择日期):

在界面上,创建一个calendarWidget和一个label

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

#include <QDebug>
#include <QDate>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}


void Widget::on_calendarWidget_selectionChanged()
{
    QDate date = ui->calendarWidget->selectedDate();
    qDebug() << date;

    ui->label->setText(date.toString());
}

运行结果:

相关推荐
西游音月1 小时前
(12)功能实现:Qt实战项目之读写配置文件
开发语言·qt
VBA63372 小时前
VBA之Word应用第四章第五节:段落Paragraph对象的属性(一)
开发语言
whltaoin2 小时前
【Java SE】Java IO体系深度剖析:从原理到实战的全方位讲解(包含流操作、序列化与 NIO 优化技巧)
java·开发语言·nio·se·io体系
csbysj20206 小时前
jQuery 删除元素
开发语言
xxy-mm6 小时前
Javascript 中的继承
开发语言·javascript·ecmascript
quikai19818 小时前
python练习第二组
开发语言·python
AI视觉网奇8 小时前
Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr
开发语言·c++·算法
wjs20248 小时前
并查集快速合并
开发语言
free-elcmacom8 小时前
MATLAB与高等数学<1>一道曲面积分题的几何直观
开发语言·数学建模·matlab·高等数学