6. 基础控件和布局

Qt的GUI程序设计有丰富的界面组件,统称为widget组件,而且所有界面组件类大多数都是间接或者直接继承于 QWidget 类。 在Qt Designer界面,我们可以看到很多基础的界面组件:

class001

上面基础组件大致分为:按钮类、输入类、显示类、容器类、布局管理、模型/视图和其他组件。

Qt的基础控件统一由QWidget派生出来,其中:

  • QAbstractButton、QAbstractSlider、QAbstractSpinBox为三个抽象类,分别抽象出按键、滑块、旋钮控件的基本特性,进而由其子类实现成具体的控件。

  • QDialog为Qt中的对话框,具体表现有文件对话框,字体对话框,颜色对话框等一系列组件.

  • QFrame是存放架构控件的基类,进一步派生出了QLabel,QTextEdit等等控件

  • 除此以外还有很多具备特定功能的高级控件,比如涉及到容器,模型/视图的View控件等等。

在此之前我们使用了一些控件(类),需要了解这些控件(类)的详细信息,我们可以在Qt Creator中,点击帮助按钮(Qt Assistant),点击查找模式, 在下面的搜索栏中输入我们要查到的类或关键词。

controlui000

我们这里搜索QAbstractButton会出现如上结果,结果中就包含该类的介绍,类的属性,类的成员函数等等。 也可以在编辑界面,鼠标移动到要了解的类或者函数上面,按下快捷键F1,就可自动打开Qt Assistant,显示类或者函数的信息。

这章节主要讲解介绍 基础控件的功能,控件使用以及布局管理等

6.1. QAbstractButton

在Qt中QAbstractButton类实现一个抽象按钮,提供了按钮所共有的功能, 并且让它的子类来指定如何处理用户的动作,并指定如何绘制按钮。

任何按钮都可以显示包含文本和图标, 我们可以通过setText()来设置文本,setIcon()来设置图标。 除此以外,按钮在按下和抬起,选中和未选中,使能和失能都可以设置成不同的显示效果。

QAbstractButton提供了用于按钮的大多数状态:

  • isDown() 判断是否按下按钮。

  • isChecked() 判断按钮是否已选中,只有切换按钮才有。

  • isEnabled() 判断用户是否可以按下按钮。

  • setAutoRepeat() 设置如果用户按下按钮,按钮是否将自动重复。autoRepeatDelay和autoRepeatInterval定义如何进行自动重复。

  • setCheckable() 设置按钮是否为切换按钮。

isDown()和isChecked()之间的区别如下: 按钮按下(isdown)将不会自动弹起,表示选中状态(isChecked),需要再次点击,按钮弹起,表示未选中状态。

QAbstractButton提供了四个信号:

  • pressed() 当鼠标光标位于按钮内部时,如果按下鼠标左键,则将发出此信号

  • released() 释放鼠标左键时,产生released信号。

  • clicked() 当按钮第一次被按下随即被释放时,或者有快捷键被触发,或click()或animateClick()被调用时会产生此信号。

  • toggled() 当切换按钮的状态更改时会触发此信号。

我们在Qt Designer中使用的按钮控件,QPushButton,QToolButton,QRadioButton,QCheckBox (其中QRadioButton和QCheckBox类是勾选按钮,QPushButton和QToolButton类是点击按钮,可以切换行为) 等都是通过继承QAbstractButton,都会获得以上属性。

这些按钮类一些默认属性是不同的,例如例如QRadioButton和QCheckBox默认是Checkable(),是可选的;而QPushButton和QToolButton默认是没有Checkable(); QRadioButton默认是可以autoExclusive(一个容器或者父类中的按钮互斥),而QCheckBox默认是没有autoExclusive()。

6.1.1. QPushButton(普通按钮)

按钮或命令按钮可能是任何图形用户界面中最常用的窗口小部件。 按下(单击)按钮以命令计算机执行某些操作或回答问题,如我们经常看到的按钮:“确定”,“应用”,“取消”,“关闭”,“是”,“否”和“帮助”等。

我们在例程中使用了四个按钮,Qt原生QPushButton如下:

control001

我们将QPushButton的clicked()信号绑定到pushbutton_clicked()槽函数,当按钮被按下时就执行槽函数中的内容。

lubancat_qt_tutorial_code/Control_1/mainwindows.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
//绑定槽函数
QList<QPushButton*> btnlist = ui->stackedWidget->findChildren<QPushButton*>();
foreach(auto btn, btnlist)
{
    connect(btn,SIGNAL(clicked()),this,SLOT(pushbutton_clicked()));
}

//槽函数
void MainWindow::pushbutton_clicked()
{
    QPushButton* btn= qobject_cast<QPushButton*>(sender());
    if(btn->objectName()=="btn_2")                          // 根据属性判断是哪个按钮
    {
        if(btn->isChecked())                                // 判断是否选中
            ui->statusBar->showMessage(QString("%1按下").arg(btn->objectName()));
        else
            ui->statusBar->showMessage(QString("%1弹起").arg(btn->objectName()));
    }

    ui->statusBar->showMessage(QString("%1被点击").arg(btn->objectName()));
}

在槽函数中,通过sender()找到信号的发射者,然后使用qobject_cast执行动态强制转换。然后根据属性判断是哪个按钮按下了,然后在statusBar中显示按钮的状态, 其他更多按键的设置看下例程和Qt Assistant文档。

6.1.2. QToolButton(工具按钮)

工具按钮是一种特殊按钮,可用于快速访问特定命令或选项,与普通命令按钮不同,QToolButton能够插入默认的action、能够插入menu、能够设置图标、设置文字等。

工具按钮的图标设置为QIcon,可以为禁用状态和活动状态指定不同的图。 当按钮的功能不可用时,将使用禁用的图;当自动升高按钮时,由于鼠标指针悬停在其上,因此将显示活动的图。

QToolButton一种经典用法是选择工具,例如,绘图程序中的“笔”工具,或者是截图工具中的一排工具选择。

例程中也使用了四个工具按钮,使用方法和QPushButton差不多,主要添加了新的功能():

control002

将工具按钮的clicked()信号绑定到toolbutton_clicked(),点击工具按钮以实现特定的功能。

lubancat_qt_tutorial_codee/Control_1/mainwindows.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// QToolButton按钮
ui->tbtn_1->setCheckable(true);      //设置工具按钮1是可以被选中
//    ui->tbtn_1->setStyleSheet("QToolButton{border: 0px solid;background-color:#F65345;color:#FFFFFF;}\
//                                QToolButton:disabled{background-color:#DCDCDC;color:#FFFFFF;}\
//                                QToolButton:hover{border: 0px solid;background-color:#f67469;color:#FFFFFF;opacity:0.2;}\
//                                QToolButton:checked{background-color:#c44237;color:#FFFFFF;}");   //设置按钮2的样式,样式相关设置参考下后面章节

QAction *action = new QAction(this);
action->setText("测试");
ui->tbtn_2->setDefaultAction(action);                        //设置tool按钮2,添加action
ui->tbtn_2->setIcon(QIcon(":/take_pressed.png"));            //设置tool按钮2,添加图标
ui->tbtn_2->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); //设置文字位于图标之下
ui->tbtn_3->setFocusPolicy(Qt::NoFocus);       //设置焦点,默认点击(聚焦)这里,这里没有焦点
ui->tbtn_3->setAutoRaise(true);                //设置tool按钮3,按钮的边框会被掩藏,鼠标悬浮在该按钮上时出现。
ui->tbtn_3->setIconSize(QSize(45,45));         //设置tool按钮3的尺寸

ui->tbtn_4->setText("Up Arrow");
ui->tbtn_4->setArrowType(Qt::UpArrow);         //设置tool按钮4显示一个向上的箭头
ui->tbtn_4->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); //设置文字位于图标之下

tbtn_1 通过其 setCheckable() 将按钮设置为可保存状态的形式,并通过 isChecked() 来判断工具按钮按下弹起状态。

更多的功能和信息参考下 QToolButton类文档

6.1.3. QRadioButton(单选按钮)

QRadioButton 是一个选项按钮,可以打开(选中)或关闭(取消选中)。

通常情况下QRadioButton为单选(即默认情况下是开启了autoExclusive属性),当存在多个选项的时候,我们可以设置一组QRadioButton(将多个按钮放入QButtonGroup), 使能autoExclusive属性,这时同一组的QRadioButton只能选中一个选项。

每当按钮打开或关闭时,它都会发出change()信号。如果您想在每次按钮更改状态时触发一个动作,请连接到此信号,使用isChecked() 查看是否选择了特定按钮。

就像QPushButton一样,单选按钮可以显示文本,还可以显示一个小图标。该图标使用setIcon() 设置。 可以在构造函数中或使用setText() 设置文本。可以通过在文本中的首选字符前面加上“&”符号来指定快捷键。

control003
lubancat_qt_tutorial_code/Control_1/mainwindows.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
QList<QRadioButton*> rbtnlist = ui->stackedWidget->findChildren<QRadioButton*>();
foreach(auto rbtn, rbtnlist)
{
    connect(rbtn,SIGNAL(clicked()),this,SLOT(radiobutton_clicked()));
}

void MainWindow::radiobutton_clicked()
{
    QRadioButton* rbtn= qobject_cast<QRadioButton*>(sender());

    ui->statusBar->showMessage(QString("%1被选中").arg(rbtn->objectName()));
}

我们依然只是将QRadioButton的clicked()信号和槽连接起来,用于判断那个按钮被选中了。

6.1.4. QCheckBox(复选框)

QCheckBox 是一个选项按钮,可以打开(选中)或关闭(取消选中)。

QCheckBox则通常多为复选框,复选框通常用于表示应用程序中可以启用或禁用而不会影响其他功能的功能。

control004
lubancat_qt_tutorial_code/Control_1/mainwindows.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
QList<QCheckBox*> cboxlist = ui->stackedWidget->findChildren<QCheckBox*>();
foreach(auto cbox, cboxlist)
{
    connect(cbox,SIGNAL(stateChanged(const int)),this,SLOT(checkbox_stateChange(const int)));
}

void MainWindow::checkbox_stateChange(const int state)
{
    QCheckBox* cbox= qobject_cast<QCheckBox*>(sender());

    if(state)
        ui->statusBar->showMessage(QString("%1被选中").arg(cbox->objectName()));
    else
        ui->statusBar->showMessage(QString("%1未选中").arg(cbox->objectName()));

}

6.1.5. QComboBox(组合框)

QComboBox 提供了一种以占用最少屏幕空间的方式向用户显示选项列表的方法。

组合框是显示当前项目的选择小部件,并且可以弹出可选项目的列表。组合框可能是可编辑的,从而允许用户修改列表中的每个项目。

组合框可以包含像素图和字符串。适当地重载了insertItem()和setItemText()函数。 对于可编辑的组合框,提供了函数clearEditText(),以清除显示的字符串,而不更改组合框的内容。

如果组合框的当前项目发生更改,则会发currentIndexChanged(),currentTextChanged()和Activated()三个信号。 无论更改是通过编程方式还是通过用户交互完成,始终都会发出currentIndexChanged()和currentTextChanged(),而仅是由用户交互引起时才发出activate()。

control005
lubancat_qt_tutorial_code/Control_1/mainwindows.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
//设置comboBox选项
QStringList sizelist;
sizelist<< "12"<<"14"<<"16"<<"18"<<"20";       //设置comboBox选项
ui->comboBox->addItems(sizelist);              //添加comboBox选项

//选项改变时,设置app字体大小
void MainWindow::on_comboBox_currentIndexChanged(const QString &arg1)
{
    QFont font = qApp->font();
    font.setPointSize(arg1.toInt());
    qApp->setFont(font);
    this->update();
    ui->statusBar->showMessage(QString("当前字号:%1").arg(arg1));
}

在例程中,我们通过 addItems() 方法将我们预设的字号插入到组合框中,当我们改变组合框中的选项时, 信号和槽会传递切换之后的组合框被选中的选项中的文字,即我们设置的字号,再通过qApp->setFont(),来设置app的字号。

6.1.6. QFontComboBox

QFontComboBox 继承自QComboBox,组合框中填充了按字母顺序排列的字体系列名称列表,例如Arial,Helvetica和Times New Roman。

在使用之前我们可调用setWritingSystem()告诉QFontComboBox仅显示支持给定书写系统的字体,也可以调用setFontFilters()过滤掉某些类型的字体,例如不可缩放的字体或等宽字体。

当选择新字体时,除了currentIndexChanged()之外,还会发出currentFontChanged()信号,传递当前选项中的字体。

control006
lubancat_qt_tutorial_code/Control_1/mainwindows.cpp
1
2
3
4
5
void MainWindow::on_fontComboBox_currentFontChanged(const QFont &f)
{
    qApp->setFont(f);
    ui->statusBar->showMessage(QString("当前字体设置为:%1").arg(f.family()));
}

例程中QFontComboBox复选框选项发生改变时,我们通过setFont()设置程序的字体。

6.2. QAbstractSlider

QAbstractSlider 该类被设计为QScrollBar,QSlider和QDial之类的小部件的公共类。

这是该类的主要属性:

  • value:QAbstractSlider当前的值。

  • minimum:可能的最小值。

  • maximum:可能的最大值。

  • singleStep:抽象滑块提供的两个自然步骤中的较小者,通常对应于用户按下箭头键。

  • pageStep:抽象滑块提供的两个自然步骤中的较大者,通常对应于用户按下PageUp或PageDown的情况。

  • tracking: 是否启用了滑块跟踪。

  • slidePosition:滑块的当前位置。如果启用了跟踪(默认设置),则此值与value相同。

QAbstractSlider有如下信号:

  • valueChanged() value值已更改。

  • slidePressed() 用户开始拖动滑块。

  • slideMoved() 用户拖动滑块。

  • slideReleased() 用户释放滑块。

  • actionTriggered() 触发了滑块操作。

  • rangeChanged() minimum-maximum范围已更改。

6.2.1. QProgressBar

进度条用于向用户指示操作的进度,并向他们显示应用程序仍在运行。

进度条使用通常先指定最小和最大可能的步长值,然后设置当前得值,它最终将显示已完成的百分比。 百分比是通过 (value-minimum)/(maximum-minimum) 计算得出的。

control011

6.2.2. QSlider

滑块是用于控制有界值的经典小部件。它使用户可以沿水平或垂直凹槽移动滑动手柄,并将手柄的位置转换为合法范围内的整数值。

Qt中QSlider原生控件如下:

control008

在例程中,我们进入Qt Designer界面,点击最上方的 编辑信号/槽,进入信号与槽的连接:

controlui001

我们直接将QSlider的 valueChanged(int) 信号和QProgressBar的 setValue(int) 进行绑定

controlui001

最终效果就是当我们拖动滑块的手柄,进度条也跟着变化。

6.2.3. QScrollBar

QScrollBar 是滚动条,使用户能够访问文档中大于用于显示文档的窗口小部件的部分。它提供了用户在文档中当前位置以及可见文档数量的视觉指示。 滚动条通常配有其他控件,可以实现更准确的导航。

control009

在例程中我们使用了一个定时器,每隔1s调整滚动条的值。

lubancat_qt_tutorial_code/Control_1/mainwindows.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
QTimer *timer = new QTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(handletimeout()));
timer->start(1000);

void MainWindow::handletimeout()
{
    static int curr=0;
    ui->horizontalScrollBar->setValue(curr);
    ui->verticalScrollBar->setValue(curr);
    if(curr++>ui->horizontalScrollBar->maximum())
        curr=0;
}

6.2.4. QDial QLcdNumber

当用户需要将值控制在程序可定义的范围内,并且该范围可以环绕(例如,角度范围从0到359度)或对话框布局需要方形小部件时,可以使用QDial。

由于QDial继承自QAbstractSlider,因此刻度盘的行为与滑块类似。当wrap()为false(默认设置)时,滑块和拨盘之间没有真正的区别。 它们都共享相同的信号,插槽和成员功能。您使用哪一种取决于用户的期望和应用程序的类型。

QLcdNumber可以显示几乎任何大小的数字。它可以显示十进制,十六进制,八进制或二进制数字。 使用display()插槽可以很容易地连接到数据源,该槽函数可以重载五种参数类型中的任何一种。

在例程中,我们直接将QDial的 sliderMoved(int) 信号和display(int)进行绑定,当滑动QDial时,QLcdNumber上面的值就会随之改变。

control010
lubancat_qt_tutorial_code/Control_1/mainwindows.cpp
1
2
//设置QDial值得范围
ui->dial->setRange(0,255);

6.3. QAbstractSpinBox

QAbstractSpinBox 是QSpinBox,QDoubleSpinBox和QDateTimeEdit的抽象类。

这是该类的主要属性:

  • text:在控件中显示的文本。

  • alignment:文本的对齐方式。

  • wrapping: 否从最小值包装到最大值。

QAbstractSpinBox提供了一个虚拟的stepBy()函数,只要用户触发了一个步骤,就会调用该函数。此函数采用整数值表示已执行了多少步骤

6.3.1. QSpinBox和QDoubleSpinBox

QSpinBox旨在处理整数和离散值集(例如,月份名称);使用QDoubleSpinBox作为浮点值。

QSpinBox允许用户通过单击上/下按钮或按键盘上的上/下按钮来选择/选择一个值,以增加/减少当前显示的值,用户也可以手动输入该值。 旋转框支持整数值,但可以扩展为使用带有validate(),textFromValue()和valueFromText()的不同字符串。

每次值更改时,QSpinBox(QDoubleSpinBox)都会发出valueChanged()和textChanged()信号,前者提供一个int(double)值,而后者提供一个QString。 当前值可以使用value() 获取当,也可以使用setValue() 对当前值进行设置。

control007

在例程中,我们使用了两个spinBox和一个doubleSpinBox,分别用来表示颜色的RGB值, 对每个控件值的范围进行限定,限定值为0-255。

采用 on_xxxx_valueChanged() 的形式,绑定信号和槽。当控件的值发生改变时,就会执行槽函数中的内容。

lubancat_qt_tutorial_code/Control_1/mainwindows.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
ui->spinBox_R->setRange(0,255);        // 设置spinBox_R数值范围0~255
ui->spinBox_G->setRange(0,255);        // 设置spinBox_G数值范围0~255
ui->doubleSpinBox_B->setRange(0,255);  // 设置spinBox_B数值范围0~255

ui->spinBox_R->setValue(0);            // 设置spinBox_B默认值
ui->spinBox_G->setValue(0);            // 设置spinBox_B默认值
ui->doubleSpinBox_B->setValue(0);      // 设置spinBox_B默认值


//设置字体颜色,作用在父类gBox_Box中
void MainWindow::on_spinBox_R_valueChanged(int )
{
    ui->gBox_Box->setStyleSheet(QString("color:rgb(%1, %2, %3)").arg(ui->spinBox_R->value()).arg(ui->spinBox_G->value()).arg(ui->doubleSpinBox_B->value()));
    ui->statusBar->showMessage(QString("字体颜色:rgb(%1, %2, %3)").arg(ui->spinBox_R->value()).arg(ui->spinBox_G->value()).arg(ui->doubleSpinBox_B->value()));
}
void MainWindow::on_spinBox_G_valueChanged(int )
{
    ui->gBox_Box->setStyleSheet(QString("color:rgb(%1, %2, %3)").arg(ui->spinBox_R->value()).arg(ui->spinBox_G->value()).arg(ui->doubleSpinBox_B->value()));
    ui->statusBar->showMessage(QString("字体颜色:rgb(%1, %2, %3)").arg(ui->spinBox_R->value()).arg(ui->spinBox_G->value()).arg(ui->doubleSpinBox_B->value()));
}

void MainWindow::on_doubleSpinBox_B_valueChanged(double )
{
    ui->gBox_Box->setStyleSheet(QString("color:rgb(%1, %2, %3)").arg(ui->spinBox_R->value()).arg(ui->spinBox_G->value()).arg(ui->doubleSpinBox_B->value()));
    ui->statusBar->showMessage(QString("字体颜色:rgb(%1, %2, %3)").arg(ui->spinBox_R->value()).arg(ui->spinBox_G->value()).arg(ui->doubleSpinBox_B->value()));
}

在槽函数中,我们通过QSS来设置样使表,修改字体显示的颜色:

control007

6.3.2. QDateTimeEdit

QDateTimeEdit 允许用户通过使用键盘或箭头键来增加和减少日期和时间值来编辑日期, 箭头键可用于在QDateTimeEdit框中的一个区域移动。

日期和时间可设置的显示格式,比如 yyyy年MM月dd日 hh:mm:ss 最终显示为 2021年3月20日 14:08:56, yyyy-MM-dd hh:mm:ss 最终显示为2021-3-20 14:08:56,格式是通过setDisplayFormat()函数来设置。

control012
lubancat_qt_tutorial_code/Control_1/mainwindows.cpp
1
2
3
4
5
6
7
ui->dateEdit->setDisplayFormat("yyyy年MM月dd日");
ui->dateEdit->setDate(QDate::currentDate());

void MainWindow::on_dateTimeEdit_dateTimeChanged(const QDateTime &dateTime)
{
    ui->statusBar->showMessage(QString("当前时间日期:%1").arg(dateTime.toString("yyyy-MM-dd hh:mm:ss")));
}

Qt中用QDateTime这个类来记录时间日期,我们可以使用QDateTime::currentDateTime()获取当前的时间日期, 也可以用toString()方法来转成字符串。

6.3.3. QCalendarWidget

Qt提供一个日历的小窗口,显示当前的日期。

control013
lubancat_qt_tutorial_code/Control_1/mainwindows.cpp
1
2
3
4
5
6
//QCalendarWidget改变时,在状态栏显示QCalendarWidget的日期
void MainWindow::on_calendarWidget_clicked(const QDate &date)
{
    ui->dateEdit->setDate(date);
    ui->statusBar->showMessage(QString("当前日期:%1").arg(date.toString("yyyy-MM-dd")));
}

6.3.4. QTimeEdit和QDateEdit

QTimeEdit、QDateEdit是QDateTimeEdit的子类,一个用于设置时间,一个用于设置日期。

lubancat_qt_tutorial_code/Control_1/mainwindows.cpp
1
2
3
4
5
6
7
ui->timeEdit->setDisplayFormat("hh:mm:ss");
ui->timeEdit->setTime(QTime::currentTime());

void MainWindow::on_timeEdit_userTimeChanged(const QTime &time)
{
    ui->statusBar->showMessage(QString("当前时间:%1").arg(time.toString("hh:mm:ss")));
}
lubancat_qt_tutorial_code/Control_1/mainwindows.cpp
1
2
3
4
5
6
7
8
9
ui->dateTimeEdit->setDisplayFormat("yyyy年MM月dd日 hh:mm:ss");
ui->dateTimeEdit->setDateTime(QDateTime::currentDateTime());

//关联
void MainWindow::on_dateEdit_userDateChanged(const QDate &date)
{
    ui->calendarWidget->setSelectedDate(date);
    ui->statusBar->showMessage(QString("当前日期:%1").arg(date.toString("yyyy-MM-dd")));
}

在示例中,我们将QDateEdit和QCalendarWidget关联起来,当两个控件中的任意一个控件的日期改变时, 另一个控件对应的日期也会发生相应的改变。

6.4. 其他基础控件

6.4.1. QLabel

QLabel用于显示文本或图像,没有提供用户交互功能。

control014
lubancat_qt_tutorial_code/Control_1/mainwindows.cpp
1
2
3
4
5
6
7
8
9
ui->lab_1->setText("123");
QPixmap pix(":/images/camera/take_pressed.png");
ui->lab_2->setPixmap(pix);
QMovie *movie = new QMovie(":/images/etc/2021-3-20.gif");
movie->setScaledSize(ui->lab_2->size());
movie->start();
ui->lab_3->setMovie(movie);
ui->lab_3->setScaledContents(true);
ui->lab_4->setText("<p style=\"color:red;font-size:16px;\"> hello <b style=\"color:black;font-size:22px;\">QLabel</b></p>");
controlui002

在例程中,我们通过 setText() 来设置文字,setPixmap() 来设置图片; setMovie() 来设置GIF动画,我们还可以通过html来显示更多样式的文字。

6.4.2. QLineEdit

行编辑允许用户使用有用的编辑功能集合输入和编辑一行纯文本,包括撤消和重做,剪切和粘贴以及拖放

我们可以通过设置相关的属性来实现某些功能,比如 setEchoMode(QLineEdit::Password) ,将设置QLineEdit的回显为passwd模式; setPlaceholderText() 可设置提示文本;除此以外还可以使用正则表达式来限制输入。

control015

在例程中,我们将LineEdit的 textEdited(const QString)returnPressed() 信号分别绑定到槽函数 lineedit_textEdited(const QString)lineedit_returnPressed() ; 当我们编辑LineEdit的文字的时候,编辑的文字会被显示到plainTextEdit中, 当我们在LineEdit中点击回车时,控件中的文字会被追加到textBrowser中。

lubancat_qt_tutorial_code/Control_1/mainwindows.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
ui->lineEdit_1->setText("123");
ui->lineEdit_2->setPlaceholderText("默认显示");
QRegExp regx("[0-9]+12");
ui->lineEdit_3->setValidator(new QRegExpValidator(regx, this));
ui->lineEdit_4->setEchoMode(QLineEdit::Password);

QList<QLineEdit*> linelist = ui->stackedWidget->findChildren<QLineEdit*>();
foreach(auto line, linelist)
{
    connect(line,SIGNAL(textEdited(const QString)),this,SLOT(lineedit_textEdited(const QString)));
    connect(line,SIGNAL(returnPressed()),this,SLOT(lineedit_returnPressed()));
}

void MainWindow::lineedit_returnPressed()
{
    QLineEdit* line= qobject_cast<QLineEdit*>(sender());
    QString str= line->text();
    if(str.isEmpty())
        return;

    line->clear();
    ui->plainTextEdit->appendPlainText(">>> "+ str);


void MainWindow::lineedit_textEdited(const QString str)
{
    //QLineEdit* line= qobject_cast<QLineEdit*>(sender());
    ui->textBrowser->clear();
    ui->textBrowser->append(str);
}

6.4.3. QTextEdit

QTextEdit是一种高级WYSIWYG查看器/编辑器,支持使用HTML样式的标记或Markdown格式的富文本格式。 它经过优化,可处理大型文档并快速响应用户输入。

QTextEdit适用于段落和字符。段落是经过格式化的字符串,将其自动换行以适合窗口小部件的宽度。 默认情况下,阅读纯文本时,一个换行符表示一个段落。一个文档包含零个或多个段落。 段落中的单词根据段落的对齐方式对齐。段落之间用强行换行符分隔。段落中的每个字符都有其自己的属性,例如字体和颜色。

QTextEdit可以显示图像,列表和表格。如果文本太大而无法在文本编辑的视口中查看,则会出现滚动条。 文本编辑可以加载纯文本文件和富文本文件。富文本可以使用HTML 4标记的子集来描述;

Qt中的富文本格式支持旨在提供一种快速,便携式和高效的方法,为应用程序添加合理的在线帮助功能,并为富文本编辑器提供基础支持。

control016

6.4.4. QTextBrowser

QTextBrowser扩展了QTextEdit(以只读模式),并添加了一些导航功能,以便用户可以跟踪超文本文档中的链接。

6.4.5. QPlainTextEdit

QPlainTextEdit是支持纯文本的高级查看器/编辑器。它经过优化,可处理大型文档并快速响应用户输入。 QPlainText使用与QTextEdit几乎相同的技术和概念,但针对纯文本处理进行了优化。

6.5. 布局管理

在Qt Designer 设计GUI界面时,为了使界面美观,需要使窗体上的所有控件有以一个合适的位置和大小。我们通常是通过布局管理器、使用容器、分割条来布局,也可以手动布局。

6.5.1. 布局管理器

在Qt Designer,提供了窗口布局:Vertical Layout(垂直布局) ,Horizontal Layout(水平布局),Grid Layout(栅格布局),Form Layout(表单布局)等。 其中QHBoxLayout、QVBoxLayout继承自 QBoxLayout, QBoxLayout、QGridLayout继承自QLayout, QLayout继承自QObject和QLayoutItem。

control04

使用代码化布局,代码设计布局效率低,且界面复杂时编码难度大。 码化布局常用的方法是通过 addWidget()和addLayout(),如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
void Widget::iniUI()
{
    //创建文本框
    textEdit = new QTextEdit;
    textEdit->setReadOnly(1);   //设置只读

    //创建lineEdit框和一个按钮控件
    lineEdit = new QLineEdit;
    BtnSend = new QPushButton("发送");

    //创建一个水平布局,通过addWidget()添加BtnSend和lineEdit
    QHBoxLayout *HLay =new QHBoxLayout;
    HLay->addWidget(lineEdit);
    HLay->addWidget(BtnSend);

    //创建垂直布局,并设置为主布局,通过addLayout()添加布局HLay和通过addWidget()添加textEdit
    QVBoxLayout *VLay=new QVBoxLayout(this);
    VLay->addWidget(textEdit);         //添加textEdit
    VLay->addLayout(HLay);             //添加HLay

    setLayout(VLay);    //设置为窗口的主布局
}

后面示例都是在Qt Designer上可视化操作。

1、 垂直布局

选中控件,然后点击上方的垂直布局(UI设计器工具栏),或者从左边组件栏中拉一个垂直布局组件(QVBoxLayout)到窗口,然后添加控件:

control04

垂直布局内的控件,将自动在垂直方向上分布。

2、 水平布局

选中控件,然后点击上方的水平布局(UI设计器工具栏),或者从左边组件栏中拉一个水平布局组件(QHBoxLayout)到窗口,然后添加控件:

control04

水平布局内的控件,将自动在水平方向上分布。

3、 栅格布局

选中控件,然后点击上方的栅格布局(UI设计器工具栏),或者从左边组件栏中拉一个栅格布局组件(QGridLayout)到窗口,然后添加控件:

control04

栅格布局内的控件,将自动在网格方向上分布(划分行和列),没有控件的网格为空。

4、 表单布局

选中控件,然后点击上方的表单布局(UI设计器工具栏),或者从左边组件栏中拉一个表单布局组件(QFormLayout)到窗口,然后添加控件:

control04

和栅格布局类似,但只有最右侧的一列网格会改变大小。

5、 控件大小策略

在Qwidget类或者直接/间接继承于Qwidget的类,都有属性sizePolicy属性,它定义了组件在水平方向或者垂直方向的尺寸伸展策略。 这里点击一个单选按钮(QRadioButton),在属性框可以看到:

control04

水平策略(Horizontal Policy)表示组件在水平上的尺寸策略,垂直策略(Vertical Policy)表示组件在垂直方向上的尺寸变化策略。

sizePolicy属性是 QsizePolicy 类型,值是QsizePolicy::Policy,各枚举值含义看下下面表格:

sizeType属性

说明

Fixed

0

固定值策略,根据QWidget::sizeHint()函数返回组件建议尺寸设置,在布局管理中组件尺寸不会变化

Minimum

GrowFlag

指定最小值策略,设置一个组件可以缩小的最小尺寸,使用QWidget::sizeHint()函数返回值作为最小尺寸

Maximum

ShrinkFlag

指定最大值策略,设置一个组件可以放大的最打尺寸,使用QWidget::sizeHint()函数返回值作为最大尺寸

Preferred

GrowFlag|ShrinkFlag

首选项策略,使用QWidget::sizeHint()函数返回值作为最优尺寸,组件可以缩放放大不超过sizeHint()函数返回值

Expanding

GrowFlag|ShrinkFlag|ExpandFlag

可扩展策略,sizeHint()函数返回值是可变的尺寸,组件可放大缩小

MinimumExpanding

GrowFlag|ExpandFlag

最小可扩展策略,sizeHint()函数返回值是最小尺寸,组件可扩展

Ignored

ShrinkFlag|GrowFlag|IgnoreFlag

Qwidget::sizeHint()对应的缺省大小将会被忽略,控件将会获取尽可能多的空间

设置水平/垂直策略时,QWidget::sizeHint()函数会起到很大作用,它是组件作为在布局管理器中部件的缺省大小,既建议值(也是缺省值),其他组件该值不可修改, (Spacer组件可修改),组件实际大小受部件的大小策略、sizeHint以及布局中其他部件的影响。

我们一般布局,组件都是使用sizePolicy的默认值,比如前面图片的单选按钮(QRadioButton),其水平策略是Minimum,垂直方向是Fixed,表示水平方向可扩展,垂直方向是固定尺寸。

在前面的sizePolicy属性图片中,还有两项是水平伸展(Horizontal Stretch)和垂直伸展(Vertical Stretch)。 水平伸展(Horizontal Stretch)表示水平方向的伸展因子,伸缩比例;垂直伸展(Vertical Stretch)表示垂直方向的伸展因子,伸缩比例; 它们默认值都是0(0~255),表示组件保持默认的长宽。

我们在一个空白窗体上拖入一个分组框(Group Box),然后加入三个水平布局(Horizontal Layout),再在三个水平布局中加入普通按钮(QPushButton)。 之后,我们设置下三个水平布局(Horizontal Layout)中普通按钮的不同水平伸展(Horizontal Stretch),直接在Qt Designer中修改:

control04

当我们放大或者缩小分组框(Group Box)时,普通按钮将按水平伸展值缩放:

control04

可以看到水平布局(Horizontal Layout)中普通按钮设置不同水平伸展(Horizontal Stretch)值,放大缩小窗口时,显示的按钮水平方向尺寸伸缩不一致(根据水平伸展设置的值)。 垂直伸展(Vertical Stretch)也是类似效果,读者可以自行Qt Designer上测试下。

6.6. 例程说明

编写一个例程,主要测试Qt Designer的基础控件:

  • 首先创建一个空工程;

  • 在UI放置stackedWidget作为主窗口;

  • 将我们要演示的控件分别添加到stackedWidget中;

  • 通过菜单栏对stackedWidget显示页面进行切换,显示不同控件;

  • 代码中将控件的信号绑定到自定义的槽;

  • 在自定义的槽函数中实现该控件的常用示例。

6.6.1. 代码讲解

在介绍控件的时候,已经对控件使用的代码进行了讲解。 这里讲解一下,信号与槽的绑定,页面的切换等。

lubancat_qt_tutorial_code/Control_1/mainwindows.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
void MainWindow::inttControl()
{
    connect(ui->action_bar,SIGNAL(triggered()),this,SLOT(on_setCurrentIndex()));
    connect(ui->action_btn,SIGNAL(triggered()),this,SLOT(on_setCurrentIndex()));
    connect(ui->action_datetime,SIGNAL(triggered()),this,SLOT(on_setCurrentIndex()));
    connect(ui->action_etc,SIGNAL(triggered()),this,SLOT(on_setCurrentIndex()));
    connect(ui->action_text,SIGNAL(triggered()),this,SLOT(on_setCurrentIndex()));
    connect(ui->action_about,SIGNAL(triggered()),this,SLOT(on_setCurrentIndex()));
    connect(ui->action_exit,SIGNAL(triggered()),this,SLOT(on_setCurrentIndex()));

    QList<QPushButton*> btnlist = ui->stackedWidget->findChildren<QPushButton*>();
    foreach(auto btn, btnlist)
    {
        connect(btn,SIGNAL(clicked()),this,SLOT(pushbutton_clicked()));
    }
    ...
}

在初始化控件的时候,我们对控件的信号进行绑定。 UI上面创建了7个菜单,用代码将七个菜单的触发信号绑定到on_setCurrentIndex()槽函数上, 当菜单被点击的时候,就会调用on_setCurrentIndex()。

lubancat_qt_tutorial_code/Control_1/mainwindows.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void MainWindow::on_setCurrentIndex()
{
    QAction* action= qobject_cast<QAction*>(sender());
    //QString actionName = QObject::sender()->objectName();
    //qDebug()<<actionName;

    if(action->objectName()=="action_datetime")
        ui->stackedWidget->setCurrentIndex(0);
    else if(action->objectName()=="action_etc")
        ui->stackedWidget->setCurrentIndex(1);
    else if(action->objectName()=="action_btn")
        ui->stackedWidget->setCurrentIndex(2);
    else if(action->objectName()=="action_text")
        ui->stackedWidget->setCurrentIndex(3);
    else if(action->objectName()=="action_bar")
        ui->stackedWidget->setCurrentIndex(4);
    else if(action->objectName()=="action_about")
    {
        QString dlgTitle="about Qt";
        QMessageBox::aboutQt(this, dlgTitle);
    }
    else if(action->objectName()=="action_exit")
        exit(0);

    ui->statusBar->showMessage(QString("切换到:%1页").arg(action->text()));
}

在on_setCurrentIndex()中,我们通过qobject_cast获取信号的来源,并通过信号的objectName属性来判断,具体哪一个菜单被点击。 并分别实现不同操作,比如点击退出,执行exit(0),结束程序运行;点击关于Qt,就弹出About Qt弹窗;其他菜单项被点击则会切换到相应的页面展示控件。

6.6.2. 编译运行

6.6.2.1. PC ubuntu20.04上实验

直接点击编译并运行程序,结果如下:

controlui003

运行测试显示:

controlui003

6.6.2.2. LubanCat板卡上实验

使用 Lubancat_rk_debian10 套件编译程序之后,需要将程序拷贝到LubanCat开发板中,可通过NFS或SCP命令 NFS环境搭建 参考这里

编译好的程序在 lubancat_qt_tutorial_code/app_bin 目录中,通过scp命令将编译好的程序拉到LubanCat:

# 使用scp命令传输文件
scp cat@192.168.103.120:/home/lubancat_qt_tutorial_code/Base/xxx/Control /home/cat

然后配置好环境,运行程序:

# 在终端执行命令
cat@lubancat:~$ ./Control -platform xcb

另外,也可以直接远程连接部署,设置下环境(需要根据自己编译的qt库路径,测试使用xcb):

controlui003

然后点击编译运行:

controlui003