显示类控件
- 1. QLabel——标签
- 1.1 setPixmap设置图片
- 1.2 setAlignment设置文本对齐方式
- 1.3 setWordWrap设置自动换行
- 1.4 setIndent设置缩进
- 1.5 setMargin设置边距
- 1.6 body
- 2. QLCDNumber
- 2.1 使用QTimer实现一个倒计时效果
- 2.2 使用循环的方式实现倒计时
- 3. QProgressBar——进度条
- 4. QCalendarWidget——日历
1. QLabel——标签
QLabel可以用来显示文本和图片:
属性 | 说明 |
---|---|
text | QLabel中的文本 |
textFormat | 文本格式:1:Qt::PlaintText纯文本,2:RichText富文本,3:MarkDownText markdown格式,4:Qt:AutoText根据文本内容自动决定文本格式 |
pixmap | QLabel内的图片 |
scaldContents | 设置未true表示自动拉伸填充QLabel,设置为false则不会自动填充 |
alignment | 表示对齐方式,可以设置为水平对齐和垂直对齐 |
wordWrap | 自动换行设置,设置为true表示开启,设置为false表示不开启 |
indent | 文本缩进,水平和垂直都可以生效 |
margin | 表示文本和边框之间的距离,在上下左右四个方向生效 |
openExternalLinks | 是否允许打开一个外部链接(当Qlabel涉及到url的时候) |
buddy | 给QLabel关联一个“伙伴”,当点击QLabel时就会激活对应的“伙伴” |
1.1 setPixmap设置图片
代码样例:Label显示图片
虽然前面讲的QPushButton也可以通过setIcon的方式进行添加图片,但是大多数时候我们更希望使用QLabel来进行添加一个更单纯的一个图片。
- 首先同样讲图片以qrc的方式添加到Qt中
- 创建Label,并可以通过pixmap进行添加图片
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QLabel* label = new QLabel(this);
// 设置标签的大小和Widget窗口的大小是一样的
QRect rect = this->geometry();
label->setGeometry(0, 0, rect.width(), rect.height());
// 插入图片并设置图片大小
QPixmap pixmap(":/qt.jpg");
pixmap = pixmap.scaled(rect.width(), rect.height());
// 也可以直接使用自动填充,让图片自动贴合标签的大小
// label->setScaledContents(true);
label->setPixmap(pixmap);
}
但是这个时候我们是可以对Widget的窗口进行调整大小的,而在我们进行调整大小的时候,我们的标签是不会跟着改变的,所以这里我们需要提供一个事件。也就是说当我们在调整Widget的时候QWidget会触发一个事件resizeEvent事件,也就是说当Widget窗口大小发生变化的时候QWidget 会自动进行调用,而这个时候我们想要Widget进行调用,就可以在Widget中重写这个resizeEvent实现多态,这样只要窗口发生变化就会通过多态调用Widget中的resizeEvent(这个事件我们后面会详细进行讲解),所以我们就可以自定义函数来完成这一操作。
// 在widget.h中添加函数声明
void resizeEvent(QResizeEvent *event); // 这个是QWidget中有的
void Widget::resizeEvent(QResizeEvent *event)
{
label->setGeometry(0, 0, event->size().width(), event->size().height());
label->setScaledContents(true);
qDebug() << event->size();
}
1.2 setAlignment设置文本对齐方式
首先为了我们方便观察,我么需要给label设置一个外边框,我们可以在ui界面中进行设置。
QFrame是QLabel的父类,其中frameShape属性用来设置边框属性。
- QFrame::Box :矩形边框
- QFrame::Panel :带有可点击区域的⾯板边框
- QFrame::WinPanel :Windows风格的边框
- QFrame::HLine :水平线边框
- QFrame::VLine :垂直线边框
- QFrame::StyledPanel :带有可点击区域的⾯板边框,但样式取决于窗口主题
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->label->setText("这是一个水平居中和垂直居中");
// 设置文本的对齐方式
// 设置方式 水平方向 | 垂直方向
ui->label->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
}
而设置垂直方向和水平方向的对齐方式其实就是宏。
enum AlignmentFlag {
AlignLeft = 0x0001,
AlignLeading = AlignLeft,
AlignRight = 0x0002,
AlignTrailing = AlignRight,
AlignHCenter = 0x0004,
AlignJustify = 0x0008,
AlignAbsolute = 0x0010,
AlignHorizontal_Mask = AlignLeft | AlignRight | AlignHCenter | AlignJustify | AlignAbsolute,
AlignTop = 0x0020,
AlignBottom = 0x0040,
AlignVCenter = 0x0080,
AlignBaseline = 0x0100,
// Note that 0x100 will clash with Qt::TextSingleLine = 0x100 due to what the comment above
// this enum declaration states. However, since Qt::AlignBaseline is only used by layouts,
// it doesn't make sense to pass Qt::AlignBaseline to QPainter::drawText(), so there
// shouldn't really be any ambiguity between the two overlapping enum values.
AlignVertical_Mask = AlignTop | AlignBottom | AlignVCenter | AlignBaseline,
AlignCenter = AlignVCenter | AlignHCenter
};
1.3 setWordWrap设置自动换行
如果我们使用在label中输入了一行很长的文本而没有自动换行,就会导致部分文本被覆盖而无法显示。
ui->label->setText("这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,");
ui->label->setText("这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,");
ui->label->setWordWrap(true);
1.4 setIndent设置缩进
一般我们在书写word的时候使用的都是首行缩进,但是在Qt中,这里的缩进是对所有行都适用的,不单单是首行缩进
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->label->setText("这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,");
ui->label->setWordWrap(true);
ui->label->setIndent(50); // 单位像素
}
1.5 setMargin设置边距
我们先直接讲对齐方式改为水平左对齐,垂直上对其。
ui->label->setText("这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,");
ui->label->setWordWrap(true);
ui->label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
设置margin后
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->label->setText("这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,这是一个自动换行机制,");
ui->label->setWordWrap(true);
ui->label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
ui->label->setMargin(20);
}
1.6 body
我们可以创建两个label和两个radioButton,将其两两关联。然后可以使用快捷键的方式进行选择。
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->label->setBuddy(ui->radioButton);
ui->label_2->setBuddy(ui->radioButton_2);
}
这样就可以使用 alt + a选择1,alt+b选择2了。而如果想要做到这样的效果也需要遵循一些规则,也就是使用&A(& + 快捷键)这样的形式才可以做到。
2. QLCDNumber
QLCDNumber是一个专门用来显示数字的控件。
属性 | 说明 |
---|---|
intValue | QLCDNumber显示数字的值(int) |
value | QLCDNumber显示的数字值(double),和intValue是联动的,例如value为1.2,那么intValue就是1,另外value和intValue的方法名在位display,而不是setValue或者setValue了 |
digitCount | 显示数字的位数 |
mode | 显示数字的模式。1:QLCDNubmer::Dec,十进制。2:QLCDNubmer::Hex,十六进制。3:QLCDNubmer::Bin,二进制。4:QLCDNubmer::Oct,八进制。 |
segmentStyle | 设置显示风格,1:QLCDNumber::Flat,平面显示风格,数字呈现在一个平坦的表面上。2:QLCDNumber::Outline,轮廓显示风格,数字具有轮廓和阴影效果。3:QLCDNumber::Filled,填充风格,数字可以填充颜色与背景进行区分。 |
2.1 使用QTimer实现一个倒计时效果
代码样式:显示一个倒计时效果
在此之前介绍一个了QTimer类,这个类是Qt封装的一个定时器,可以通过start方法启动定时器,并通过传递参数的方式进行设定定时周期。而QTimer中有一个信号timeout,会根据周期定期的触发timeout信号,这样我们可以把周期设置为1秒,就可以首先一个简单的定时器了。
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建一个定时器类
time = new QTimer(this);
ui->lcdNumber->display(10);
connect(time, &QTimer::timeout, this, &Widget::handle);
// 启动定时器
time->start(1000); // 单位毫秒
}
Widget::~Widget()
{
delete ui;
}
void Widget::handle()
{
// 获取当前lcdNumnber值
int num = ui->lcdNumber->value();
if (num <= 0){
// 关闭定时器
time->stop();
return;
}
// 设置值
ui->lcdNumber->display(num - 1);
}
2.2 使用循环的方式实现倒计时
针对上述存在两个问题。
- 我们先使用最为平常的for循环进行实现,使用Sleep的方式实现。而Sleep是windows的需要包含"windows.h",那是Qt并没有相关的api,但是C++11 中引路了sleep是thread库中的sleep_for
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->lcdNumber->display(10);
int value = ui->lcdNumber->value();
while(true)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
if (value <= 0){
break;
}
value -= 1;
ui->lcdNumber->display(value);
}
}
这样明显是没法显示出来的,因为whiel循环是在Widget的构造函数中实现的,而窗口的显示是在Widget类实例化后使用w.show()进行实现的,所以是等到while循环结束后,才会调用窗口显示函数进行窗口展示。
- 所以这里我们就想到使用子线程来进行修改
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->lcdNumber->display(10);
std::thread t([this](){
int value = ui->lcdNumber->value();
while(true)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
if (value <= 0){
break;
}
value -= 1;
ui->lcdNumber->display(value);
}
});
}
上述通过子线程的方式修改GUI上的内容同样是不行的,在Qt中规定,任何对GUI上内容的操作,必须是主线程中完成的。这样的约定主要是因为GUI中的状态往往是牵一发东全身,就需要同步的对其他内容进行调整。比如调整了某个元素的尺寸,就可能影响到内部的文字位置,或者其他元素的位置.这里⼀连串的修改,都是需要按照⼀定的顺序来完成的.由于多线程执行的顺序无法保障,因此Qt从根本上禁止了其他线程修改GUI状态,避免后续的⼀系列问题.
3. QProgressBar——进度条
核心属性:
属性 | 说明 |
---|---|
minimum | 进度条最小值 |
maximum | 进度条最大值 |
value | 进度条当前值,同上 |
alignment | 文本在进度条中的对齐方式. 同上 |
textVisible | 进度条的数字是否可见 |
orientation | 进度条的方向是水平还是垂直 |
invertAppearance | 是否是朝反方向增长进度 |
textDirection | 文本的朝向. |
format | 展示的数字格式. 1:%p :表示进度的百分比(0-100),2:%v :表示进度的数值(0-100),3:%m :表示剩余时间(以毫秒为单位),4:%t :表示总时间(以毫秒为单位) |
代码样例:实现一个进度条
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
time = new QTimer(this);
connect(time, &QTimer::timeout, this, &Widget::handle);
time->start(100);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handle()
{
int value = ui->progressBar->value();
if (value >= 100){
time->stop();
return;
}
ui->progressBar->setValue(value + 1);
}
这里我们看到Widget.h这块
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void handle();
private:
Ui::Widget *ui;
QTimer* time;
};
#endif // WIDGET_H
从上述代码中我们可以发现一个小细节,就是Widget.h中明明使用了QTimer类,但是并没有包含QTimer头文件#include < QTimer >,而且还没有编译报错。这是应为Qt做了一些特殊处理,在Qt中专门有一个头文件,这个头文件包含了Qt中所有类的“前置声明”形如(class QTimer; class QWidget……),而这个头文件一般不会直接接触到,而是包含在其他的Qt的头文件中,所以在继承的时候就间接的包含这个头文件,所以就可以使用一些类在头文件中声明各种类的指针或者了引用。
而这样的做的目的就是减少编译时的运行时间,应为在C/C++编译期间是要讲头文件进行展开的,而对于一个大型的项目来讲,不乏会包含大量的头文件,而这也间接的增加了编译的时间,所以在头文件声明之处就可以使用这样的前置声明的技术来减少头文件的包含,也可以做到间接减少编译的时间。
但是在C++20后就开始使用module来代替#include,这样也是为了减少编译时的时间消耗。
同样进度条也是可以设置样式的。可以通过ui界面的熟悉面板进行设置,也可以使用代码进行设置。
4. QCalendarWidget——日历
核心属性:
属性 | 说明 |
---|---|
selectDate | 当前选中的日期,返回一个QDate类型 |
minimumDate | 最小日期 |
maximumDate | 最大日期 |
firstDayOfWeek | 每周的第⼀天(也就是日历的第⼀列)是周几. |
gridVisible | 是否显示表格的边框 |
selectionMode | 是否允许选择日期 |
navigationBarVisible | 日历上方标题是否显示 |
horizontalHeaderFormat | 日历上方标题显示的日期格式 |
verticalHeaderFormat | 日历第⼀列显示的内容格式 |
dateEditEnabled | 是否允许日期被编辑 |
重要信号:
信号 | 说明 |
---|---|
selectionChanged(const QDate&) | 当选中的日期发生改变时发出 |
activated(const QDate&) | 当双击⼀个有效的日期或者按下回车键时发出,形参是⼀个QDate类型,保存了选中的日期 |
currentPageChanged(int, int) | 当年份月份改变时发出,形参表示改变后的新年份和月份 |
void Widget::on_calendarWidget_selectionChanged()
{
QDate date = ui->calendarWidget->selectedDate();
ui->label->setText(date.toString());
}