QT之常用控件

一个图形化界面当然需要有各种各样的控件,QT也不例外,在QT designer中就有提供各种各样的控件,用以开发图形化界面。

而想使用好一个QT控件,就需要了解这些控件。

QWidget

在QT中,所有控件都继承自 QWidget 类,如按钮、输入框、文本框之类的控件,都继承自该类。

换言之,该类包含所有控件通用的部分。

在QT desigener 中,我们每使用一个控件,都可以在右下角看到对应的 QWidget 属性。

这些核心属性我们可以在右下角的表中修改,也可以通过函数修改。

接下来我们了解一些 QWidget 类的核心属性。

enabled属性

|--------------|-----------------------------|
| API 名称 | 说明 |
| isEnabled() | 获取控件的可用状态 |
| setEnabled() | 设置控件是否可用,true表示可用,false表示禁用 |

给一个按钮设置一个槽函数,将控件状态设置为不可用。

发现点击了一次后,按钮就不可用了。

在 QT Designer 界面,我们能够看到一个控件其有两个父类------QObject 和 QWidget。

QObject 是 QWidget 的父类,它最主要的属性就是 objectName ,在QT中,objectName是QT辨识对象的唯一标识,互相之间不可重复,在QT Designer 中可以通过objectName获取一个对象。

这是因为 designer 的 ui 文件本来是xml格式的,通过 .qmake 编译器将 xml 文件转换成 C++的 .h 头文件(build文件中),由此构成一个 ui_widget 类。这样每个控件的 objectName 就会变成 ui_widget 类的属性成员,因此 UI::Widget *ui这个成员就能够通过 objectName 访问对应的控件对象了。

geometry属性

这个属性包含四个属性的统称。

  • x 横坐标
  • y 纵坐标
  • width 宽度
  • height 高度

其中,x和y坐标是指一个控件左上角相对于该控件的父元素的左上角的距离。

比如我们设置一个按钮,其坐标应该是这样的。

其中,由于QT都是左手坐标系,因此使用的时候都需要从左上角开始计算。

而我们可以通过API来设置属性。

|--------------------------------------------------------------------------|------------------------------------------------|
| API | 说明 |
| geometry | 获取控件的位置和尺寸,返回一个QRect对象,包含 x、y、height、width四个属性 |
| setGeometry(QRect) setGeometry(int x,int y,int width,int height) | 设置属性有两种方式:直接通过 QRect 对象设置,或者直接通过传递 int 变量设置。 |

接着我们示例一下,首先是直接通过QRect变换按钮位置。

我们发现 x 确实修改了,但是按钮的长和宽都改变了,而如果不想修改按钮的长宽就需要采用其他方案。

发现确实是整个都移动了。

frame的影响

QWidget 是一个窗口,包括标题栏、最小化、最大化、关闭按钮等,因此计算尺寸和坐标时有两种算法,包括 frame 的和不包括 frame 的。

|------------------------|-------------------------------------------------------------------------|
| API | 说明 |
| x() | 获取横坐标 包括 window frame |
| y() | 获取纵坐标 包括 window frame |
| pos() | 返回 QPoint 对象,包括 x(),y(),setX(),setY()等方法 包括 window frame |
| frameSize() | 返回QSize 对象,包括 width(),height().setWidth(),setHeight()等方法 包括window frame |
| freamGeometry() | 返回QRect对象,这个包括 x,y,width,height 等属性和修改方法 包括windowframe |
| ---------------------- | ------------------以下函数不包括window frame--------------- |
| width() | 获取宽度 |
| height() | 获取高度 |
| size() | 获取QSize对象、包含 width(),height()以及setWidth(),setHeight() 方法 |
| rect() | 获取QRect对象,包含 x,y,width,height属性以及设置函数 |
| geometry() | 获取QRect,同上 |
| setGeometry() | 设置 QRect。 |

说了这么多,frame是什么呢?实际上frame就是窗口的标题框的位置。

包含 frame 获取的坐标就是从标题框左上角开始计算,而不包含则是从下图的蓝色位置开始计算。

我们可以通过实验看一下framegeometry和 geometry 有什么区别。

一个是在构造函数时打印,一个是在按钮被按下去时打印。

发现构造函数中两个打印的位置都一样,但是函数里的不同。

这是因为构造时 Widget 还未挂在对象树上,因此不具备 Window frame,不过后槽函数运行时就以及有frame了,因此不同。

WindowTitle属性

|---------------------------------------|-----------|
| API | 说明 |
| windowTitle() | 获取控件的窗口标题 |
| setWindowTitle(const QString& title) | 设置控件的标题 |

通过这两个API可以设置标题。

WindowIcon属性

|-----------------------------------|---------------------|
| API | 说明 |
| windowIcon() | 获取控件的窗口图标,返回QIcon对象 |
| setWindowIcon(const QIcon& icon) | 设置窗口的图标 |

首先,我在d盘放入一张图片。

然后在代码中打开这张图片。

注意:这里打开文件的方式可以用 '\' 或者 '/',不过 \ 需要转义来使用, 即 \\ 的格式,因此使用的 / 。

发现图标确实改变了。

同时任务栏的图标也改变了。

不过这种方式来修改图标的方式不是很好,因为这是绝对路径,无法保证所有用户都有一样的路径。

而我们可以使用相对路径来修改图标。

  • 绝对路径:以盘符开头(windows)或 / 开头(linux) 的路径
  • 相对路径 :以 . (标识当前路径) 或以 ..(表示上级路径),使用相对路径需要明确是当前工作目录

一个QT程序,它的工作目录是变化的,如在QT Creator 运行的项目,其工作目录是项目的构建目录。

构建目录就是和QT项目并列的,用来存放临时文件和 exe 文件的目录,可以通过 Explorer 显示来找到QT项目,然后找到构建目录。

我们也可以在项目代码中直接获取工作目录。

如果我们将图片放在构建目录中,那么就能够保证每个用户都能成功换上图标。

但是!有个问题,因为构建目录很容易被删除,一旦清理项目,那么图片也就没了。

对于这种问题,QT 提供了 qrc 机制,来方便的管理项目依赖的静态资源。

qrc机制: 一种XML格式的配置文件**,用XML记录硬盘上的文件和对应的资源名,应用程序通过资源名称访问这些资源。**

通过将一些资源文件添加到项目中来管理,一般这些文件都位于qrc文件所在目录的同级目录或者子目录下

QT通过将这些文件转化了二进制数据写到 cpp 代码中,编译到 exe 文件中,使得资源与路径无关。

想要使用 qrc 来使资源和路径无关,就需要先创建一个qrc文件。

点击文件-> 新建文件或类->选择 QT 和 QT Resource File。这样就创建了一个 qrc 文件。

然后再通过给 qrc 文件添加前缀,然后添加文件即可。

添加后可以看到左边项目多出了一个文件。

然后就可以直接设置图标了。

其中,使用 qrc 机制来获取资源有它自己的规则。

  • 使用**: 开头,表示从 qrc 中读取资源**
  • /new/prefix1: **表示资源所在路径(**不可有中文或者特殊符号)
  • test.jpg : 表示资源的名称

qrc 机制实际上就是将 test.jpg 这个文件的每个字节记录下来,然后编译进exe文件中,这样文件就和资源路径不相关了。

优点:做到了资源路径和资源无关,不会丢失文件

缺点:当资源很大时,生成的exe文件也会很大,消耗的内存,编译的时间也会增加

WindowOpacity属性

|---------------------------|------------------------------------------------------------------|
| API | 说明 |
| WindowOpacity() | 获取控件的不透明数值,返回 float 。 透明度在 0.0 -> 1.0 之间,0.0 表示完全透明,1.0表示完全不透明。 |
| setWindowOpacity(float i) | 设置控件的不透明数值 |

通过这个函数我们能够获取 Window 的透明度,并且对透明度进行修改。

设置两个按钮。

然后设置槽函数分别会对MainWindow的透明度进行加和减。

我们能够看到确实修改了窗口的透明度。

而 qDebug 的输出发现结果并不一定是精确的,有一定的精度缺失。

cursor属性

|------------------------------------------------------------|---------------------------------------------------------------|
| API | 说明 |
| cursor() | 获取当前 widget 的cursor属性,返回QCursor对象 当鼠标悬停在该 widget 上时,就会显示对应的形状 |
| setCursor(const QCursor & cursor) | 设置该光标的形状,只有鼠标停留在widget上才有效 |
| QGuiApplication::setOverrideCursor(cosnt QCursor& cursor) | 设置全局的光标形状。 整个程序的光标都会改变,覆盖setCursor的内容 |

新添加一个按钮,该按钮可以修改 Cursor 属性。

该控件的槽函数会给全局的控件都改变图标。

按下按钮后,发现所有控件的图标都变成了 WaitCursor,不管是窗口的还是其他按钮的。

而 Cursor 属性有很多,通过 ctrl + 左键点击WaitCursor能够看到很多类型。

而且这个图标我们也可以自定义。

发现图标确实被修改成自定义的图片了。

font 属性

|-----------------------------|--------------------------|
| API | 说明 |
| font() | 获取QWidget对象的信息,返回QFont对象 |
| setFont(const QFont& font) | 设置该控件的字体信息 |

一个字体也是具有各种各样的属性,如字体,粗体,斜线等...

|-----------|--------------------------------|
| API | 说明 |
| family | 字体家族,有宋体,楷体,黑体等 |
| pointSize | 字体大小 |
| weight | 字体粗细,字体粗细程度从 [0,99]不等,数值越大越粗 |
| bold | 是否加粗,true 表示是,false表示否 |
| italic | 是否倾斜 |
| underline | 是否有下划线 |
| strikeOut | 是否带有删除线 |

对于字体我们可以在左下角的黄色框里面进行修改,或者通过代码修改。

可以通过代码修改

toolTip属性

|----------------------------|-------------------------------|
| API | 说明 |
| setToolTip(Qstring & str) | 设置toolTip, 当鼠标悬停时就会有提示说明 |
| setToolTipDuration(int) | 设置toolTip提示的时间,单位是ms 时间到后自动消失 |

通过两个API可以设置按钮的提示信息以及提示信息存在的时间。

focusPolicy属性

通过设置某些控件能否通过焦点获取,如通过鼠标点击或者 Tab 键选中。

焦点:能否选中这个元素

比如星际中就需要选中单位然后才能命令单位进行任务。

输入框,复选框之类的都需要这个属性。 、

|-----------------------------------|----------------------------------------------|
| API | 说明 |
| focusPolicy() | 获取该 Widget 的 focusPolicy ,返回 Qt::FocusPolicy |
| setFocusPolicy(Qt::FocusPolicy&) | 设置widget的 focusPlicy |

比如我们添加一个新控件--- lineEdit ,即单行输入框。

我们可以通过代码的形式来设置它的 FocusPolicy 属性。

而 FocusPolicy 一共有五个属性,分别对应不同的表现。

**Qt::NoFocus :**不接受焦点,即无法通过鼠标或者 Tab 键来选中控件

**Qt::TabFocus :**只能通过 Tab 键选中,无法通过鼠标选中

**Qt::ClickFocus :**只能通过鼠标选中,无法通过 Tab 键选中

**Qt::StrongFocus :**可以通过Tab 键和鼠标选中,也是控件的默认属性

**Qt::WheelFocus :**类似 StrongFocus,不过还可以通过鼠标滚轮选中,比较少用。

当然我们也能在 Qt Designer 中修改这个值。

styleSheet属性

可以通过 CSS 设置 widget 的样式。

当然,CSS是网页前端的技术,并不是Qt的技术,不过 Qt 和 CSS 也有很多类似之处,因此也引入了 CSS 的技术,但是有没有全部引入,可以说引入了,又没完全引入。

比如这里有一个文本框,可以通过右键设置,然后编辑样式表,这样就可以输出 CSS 语言来控制文本框的样式了。

在这里使用的格式也是和 CSS 一样,用键值对设置样式。

键值之间用 : 隔开 ,不同的键值对之间通过 **;**隔开。

不过 Qt 不能检测哪些不支持

而我们通过 CSS 修改的样式就是 styleSheet 属性。

当然,我们也能通过代码修改 styleSheet 属性。

|------------------------------|--------------------------|
| API | 说明 |
| setStyleSheet(QString& str) | 将 widget 的样式修改成 str 中的样式 |

比如我们设置两个按钮。

它们的槽函数会分别修改窗口的背景颜色,字体颜色。

#fff :纯白色

#333 : 深色,不是纯黑

#000 : 纯黑色

能够看到确实进行了转化。

在电脑中的所有颜色都是通过数字表示的,可以通过网站查看。在线调色板,调色板工具---在线工具 (sojson.com)

按钮类控件

PushButton控件

QPushButton 就是一个按钮,它继承自 QAbstractButton 类。

QAbstractButton 是所有按钮的父类。

我们也可以在 Qt Designer 中看到它的继承关系。

在 QAbstractButton中有些属性和 QPushButton 是高度相关的。

|--------------------|--------------------------------------------------|
| 属性 | 说明 |
| text | 按钮文本 |
| icon | 按钮图标 |
| iconSize | 图标大小 |
| shortCut | 按钮对应的快捷键 |
| autoRepeat | 按钮是否能够重复触发。 true 表示能够重复触发,false表示不能 相当于连续点击这个按钮。 |
| autoRepetDelay | 重复触发的延时时间,按住按钮之后,多久开始重复触发 |
| autoRepeatInterval | 重复触发的周期 |

由于 QWidget 算是所有控件的父类,因此在上面说的所有属性QAbustractButton 中也有。

并且 Qt 的接口设计十分清晰,比如 shorCut 属性,想设置快捷键的 API 就是 setShortcut.

可以在使用的时候自己多试试。

我们设置五个按钮,并且清空文本。

然后通过 QIcon 设置按钮图标。

然后分别个下面的四个按钮设置槽函数。

这样我们就能通过键盘来控制按钮,并且还能够设置按钮的图标。

Radio Button控件

Radio Button 是一个单选按钮,让我们在多个选项中选一个。

当然,QAbustractButton 中的所有属性它也有。

而 QAbustractButton 和 Radio Button 控件相关的属性有。

|---------------|---------------------------------------------|
| 属性 | 说明 |
| checkable | 是否能选中 |
| checked | 是否已经选中,checkable是checked的前提条件 |
| autoExclusive | 是否排他 选中一个按钮是否会取消其他按钮的选中 一般Radio Button 默认排他 |

我们设置一个文本框和三个 Radio Button 。

设置三个槽函数。

可以看到,选中一个按钮,其他的按钮就没有选中。

而如果我们设定某一个按钮是不可选中时,会发现该按钮依旧可以点击,槽函数会运行,但是不会被选中。

setCheckable 为 false

而如果是设置某个按钮 Enable 为false,则该按钮直接不可选中。

Ra

按钮的四种触发模式

在 Qt 中,按钮一共有四个触发模式。

clicked :鼠标点击并且释放触发

pressed :鼠标点击触发

released :鼠标释放触发

toggled : checked 属性更换触发

一般 Radio Button 是 toggled 模式触发。

按钮分组

Radio Button 是默认排他的,但是有时候我们需要排他性,但是又不完全排他的性质。

比如肯德基点套餐,汉堡可以牛肉堡,鸡肉堡二选一,饮料雪碧可乐二选一之类的。

我们总不可能选了牛肉堡就不能选可乐了。

这就需要按钮分组了。

我们首选搞四个按钮,设置好名字。

然后通过 QButtonGroup 进行分组,将牛肉堡和鸡肉堡放一组,可乐和雪碧放一组。

可以看到,选择汉堡的时候,不会影响到饮品的选择。

CheckBox控件

上面了解了 Radio Button,是一个单选的控件,而CheckBox控件则是一个多选控件。

该控件的属性和 RadioButton类似,API也类似,因此不再阐述。

直接了解如何使用。

在 Qt Designer 选择三个 CheckBox,并且设置好名称。

然后通过 connect 绑定对应的槽函数和信号。

对于复选框来说,每次选中或者取消选择,都会发送一个 stateChanged信号,如果想让复选框状态改变时触发某一个行为,可以绑定这个信号。

当然,我们也可以通过isChecked() 函数来判断是否变换了。

可以看到选中和取消选中槽函数都被触发了。

我们新增几个控件,然后给 PushButton 按钮添加槽函数。

当点击确认按钮就修改 label 的文本。

提交后发现确实修改了。

显示类控件

Label控件

Label 是一个标签,可以显示图片或者文本。

有以下核心属性。

|-------------------|--------------------------------------------------------------------------------------------|
| 属性 | 说明 |
| text | Label 中的文本 |
| textFormat | 文本格式 Qt::PlainText 纯文本 Qt::RichText 富文本 Qt::MarkdownText markdown 格式 Qt::AutoText 自动识别文本格式 |
| pixmap | QLabel 中的图片 |
| scaledContents | 该属性为 true 表示内容自动拉伸填充 false 则不会拉伸填充 |
| alignment | 对齐方式 可以设置水平和垂直方向对齐 |
| wordWrap | 设置为 true 文本会自动换行 false 则不会自动换行 |
| indent | 设置文本缩进,水平和垂直方向都有效 最多设置两个方向有效 具体哪两个方向取决于 alignment 属性 |
| margin | 内部文本和边框之间的边距 一般上下左右四个方向都同时有效 |
| openExternalLinks | 是否允许打开一个外部连接 如果文本是 url 时会需要 |
| buddy | 给 Qlabel 设置一个伙伴,点击 Qlabel 会激活伙伴 |

文本显示

我们设置三个标签。

分别设置为普通文本,富文本,markdown文本,然后分别用不同的语法设置一段文本。

能够看到不同的文本会按不同的语法解析文本,从而显示不同的文本。

图片显示

我们先设置一个 Label 控件。

然后将它固定在窗口的左上角,并且 label 大小为窗口大小。

同时通过 setScaledContents 来让图片铺满 label。

运行发现图片铺满了整个窗口。

但是如果我们修改窗口大小,这个图片不会随着窗口变化而变化。

这是因为这个图片只会铺满整个 label,而label又只在初始化铺满了窗口,后续窗口变换,label并没有变化。

而窗口变化实际上对应窗口的 resizeEvent 函数。

在 Qt 中,用户操作有两类概念,一个是信号,一个是事件,而 resizeEvent 也是一种事件。

而 resizeEvent 是 窗口类(MainWindow) 的父类 (QMainWindow)的一个函数,因此我们可以通过在 MainWindow 中重写 resizeEvent 类即可实现 label 随窗口改变而改变。

这也是C++的多态的一种应用。

先在头文件中声明函数。

然后定义函数,即触发 resizeEvent 时,修改 label 大小,使其跟随窗口大小变化。

此时图片是铺满 label 的,因此图片也会随着窗口变化而变化。

label的对齐、缩进和边距

label 中的文本我们可以设置 alignment 属性。

alignment 属性一共有以下几种属性。

|------------------|-------------------------------------|
| 属性 | 说明 |
| Qt::AlignLeft | 水平靠左 |
| Qt::AlignRight | 水平靠右 |
| Qt::AlignHCenter | 水平居中 |
| Qt::AlignJustify | 水平方向调整间距两端对齐 |
| Qt::AlignTop | 垂直靠上 |
| Qt::AlignButton | 垂直靠下 |
| Qt::AlignVCenter | 垂直居中 |
| Qt::AlignCenter | 居中,等价于 AlignVCenter | AlignHCenter |

Label 中的文本的对齐可以同时在水平和垂直上进行操作。

水平方向的要求 | 垂直方向的要求 即可同时控制两个方向。

一般 label 默认水平靠左,垂直居中。

并且一般情况下,label 的文本是不能自动缩进的,如果文本过长,后面的文本会看不到,因此我们可以设置自动换行。

除此之外,还可以设置缩进、边距等,我们直接来试着设置一下。

首先整四个 label,为了更好的演示,给它们设置了边框。

边框:即QFrame,QLabel 的父类,可以自行设置 frameShape。

然后给四个 Label 分别设置 Aligenment、自动换行、首行缩进、边距等设置。

cpp 复制代码
 //设置垂直水平居中文本
    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_1 居中了,label_2 换行了,label_3 有首行缩进,label_4 有边距。

其中仔细观察 label_3 和 label_4 ,发现首行缩进只对左侧和上侧有效右侧和底测无效而边距则是四个方向都有效(只不过看不出来跟底部有边距罢了) 。

buddy

label 还可以设置伙伴。

label 设置成 快捷键 &A 的形式,虽然这里没显示出 &.

关键在于 & + 快捷键的形式,如&A。

不过这样就能通过 Alt + A来选定第一个 label,同理 Alt + B 可以选定第二个 label.

Label 的 buddy 只能通过 Alt + 快捷键的方式响应,不能像 PushButton那样直接使用

然后可以在 Qt Designer 中设置 buddy,输入对应 buddy 的名称即可绑定。

也可以通过代码形式设置 buddy。

然后就能通过快捷键来选定对应的按钮。

LCD Number控件

LCD Number 是一个显示数字的控件。

|-------------------|--------------------------------------------------------------------------------------------------------------------------------------|
| 属性 | 说明 |
| intValue | 以 int 形式显示数字 |
| value | 以 double 形式显示数字 该value 和 intValue 是联动的,value 1.5 在 intValue 显示2. 而设置 intValue 和 value 的API 是 display |
| mode | 数字形式 QLCDNumber::Dec : 十进制 QLCDNumber::Hex : 十六进制 QLCDNumber::Bin : 二进制 QLCDNumber::Oct : 八进制 |
| segmentStyle | 数字显示风格 QLCDNumber::Flat :平⾯的显⽰⻛格,数字呈现在⼀个平坦的表⾯上 QLCDNumber::Outline :轮廓显⽰⻛格,数字具有清晰的轮廓和阴影效果 QLCDNumber::Filled :填充显⽰⻛格,数字被填充颜⾊并与背景区分 开 |
| digitCount | 显示几位数字 |
| smallDecimalPoint | 设置比较小的小数点 |

接着我们通过 LCDNumber 设置一个计时器。

首先在头文件添加一个 QTimer 对象和一个函数。

QTimer 对象在每一个时间间隔中都会发出一个 timeout 的信号,我们将该信号和我们创建的函数绑定在一起。

并且设置 QTimer 对象每过1秒响应一次,即时间间隔为1秒。

并且设置 lcdNumber 初识数字为3.

cpp 复制代码
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->lcdNumber->display(3);
    timer = new QTimer(this);
    connect(timer,&QTimer::timeout,this,&MainWindow::UpdateTime);
    timer->start(1000);

}

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

void MainWindow::UpdateTime()
{
    int value = ui->lcdNumber->intValue();
    if(value <= 0)
    {
        timer->stop();
        return;
    }
    ui->lcdNumber->display(value - 1);
}

而我们设定的函数则是获取 lcdNumber 的值,如果为0就退出,否则更新值为 value - 1.

可以看到确实成功了。

之前在 C++实现计时器的时候可以通过sleep + 循环的方式实现,这里呢?能否在构造函数中采用 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);
 }
}

因为循环会导致构造函数无法完成,界面无法正确构造和显示。

那如果采用线程中采用 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上的操作,都必须在主线程中完成。像 connect 函数,构造函数都是在主线程上完成的。

这是因为GUI的修改牵一发而动全身,如果可以通过线程修改一个GUI,那么其他的东西也需要修改,如字体,颜色,样式等,这需要确定一定的顺序,而多线程无法保证顺序,因此 QT 直接 ban 了这种操作。

ProgressBar控件

该控件是一个进度条。

|-----------------|------------------------------------------------------------------------------------------------------------------------------|
| 属性 | 说明 |
| minimum | 进度条最小值 |
| maximum | 进度条最大值 |
| value | 进度条当前值 |
| alignment | 文本在进度条中的对齐方式 Qt::AlignmentLeft 左对齐 Qt::AlignmentRight 右对齐 Qt::AlignmentCenter 居中对齐 Qt::AlignmentJustify 两端对齐 |
| textVisble | 进度条的数字是否可见 |
| orientation | 进度条方向是水平还是垂直 |
| invertApperance | 是否朝反方向增长 |
| textDirection | 文本的朝向 |
| format | 展示的数字格式 %p :表示进度的百分比 (1-100) **%v :**表示进度的数值(1-100) **%m:**表示剩余时间(以毫秒为单位) **%t:**表示总时间(以毫秒为单位) |

我们可以自己通过QT Designer 或者代码的形式更改进度条的形式。

和计时器一样,先设定一个定时器,和一个槽函数。

然后绑定 timeout 信号和这个槽函数。

而槽函数内部就是将进度条的数据+1.直到100;

可以看到确实从1到100的进度条了。

而颜色之类的我们也可以在QT Designer 中修改。

可以通过修改 styleSheet 修改。

不过需要注意,代码方式和这种方式只能二选一,不能既要又要哦。

设置颜色的语言有很多种,像代码方式那样用 rgb 来表示可以,通过这种方式也是可以设置的。

chunk 是选中进度条中的每一块,即背景的颜色。

而 QProgressBar::text 则是选中数字,修改数字的颜色。

Calendar Widget控件

Calendar Widget 是一个日历控件。

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

日历的重要信号

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

我们设立三个控件,projectName 分别是 label、label_2、calendarWidget 三个控件。

然后为Calendar Widget 控件添加槽函数。

可以看到确实 label 的文本修改了,换页时 label_2 的文本被修改了。

输入类控件

LineEdit 控件

|--------------------|--------------------------------------------------------------------------------------------------------------------|
| 属性 | 说明 |
| text | 输入框中的文本 |
| inputMask | 输入内容约束 可以约束输入的格式的形式 |
| maxLength | 最大长度 |
| frame | 是否添加边框 |
| echoMode | 显示方式 QLineEdit::Normal : 默认,能够显示出文本 QLineEdit::Password : 输入的文本会被 * 或者=替代 QLineEdit::NoEcho 输入的文本不会显示 |
| cursorPosition | 当前光标的位置 |
| alignment | 文字对齐方式,设置水平和垂直方向的对齐 |
| dragEnabled | 是否允许拖拽 |
| readOnly | 是否允许只读(不可修改) |
| placeHolderText | 输入框为空时,显示的提示信息 |
| clearButtonEnabled | 是否自动显示 "清除按钮" |

核心信号

|----------------------------------------|-----------------------------------------------------|
| 信号 | 说明 |
| void cursorPosition(int old,int new) | 当鼠标移动时发出信号,old 为之前的位置,new为现在的位置 |
| void editingFinished() | 当按返回键和回车键时,或者行编辑失去焦点时,发出此信号 |
| void returnPressed() | 当按返回键和回车键时发出此信号 如果设置了验证器,则必须通过验证才能发出 |
| void selectionChanged() | 当选中的文本改变时,发出该信号 |
| void textChanged(cosnt QString& text) | 当QLineEdit的文本修改时发出信号,text是新文本,代码修改能够触发该信号 |
| void textEdited(const QString& text) | 当QLineEdit的文本修改时发出信号,text是新文本,代码修改 能够触发该信号 |

通过该控件,我们可以设置一个简单的登录界面。

在 QtDesigner 中设置如下界面。

分别给三个 lineEdit 控件设置文本,并且设置好对应的 echomode .

再给pushbutton按钮设置好槽函数。

就得到了如下的界面,并且设置了清除按钮的lineEdit控件可以一次性清空数据。

不过这里有一个问题,那就是电话号码是有自己的格式的,即有11位,开头是1,后面全是数字。

如果我们提交了一个错误的电话号码,就应该是无效的。

这里我们可以通过设置正则表达式来验证电话号码是否符合格式。

首先通过正则表达式创建一个 QRegExp 对象,其正则表达式是以1开头,共11个数字。

然后给 lineEdit_telphone 设置一个 validator(验证器),其中 QValidator有四种,我们用其中一种来设置验证器 。

因为我们用到正则表达式来验证,因此用 QRegExpValidator(QRegularExpressionValidator 和这个验证器类似,不过有匹配优化)

设置好验证器后,在输入的时候 lineEdit 会发出一个信号,通过捕捉该信号,再通过槽函数即可验证是否成功。

通过 lineEdit_telphone 的验证器的 validate 函数来验证文本是否符合格式,不过由于 参数分别是 QString& 和 int& ,因此需要通过一个 QString 对象来复制一下文本,int& 的参数没什么用,验证失败的时候该参数会返回验证失败的位置。

QValidator::State 有三个参数 Invalid(违法)、Intermediate(无法判断)、Accepptable(验证成功)

在一般的登录界面上,一般可以显示出密码的内容,我们可以通过添加一个按钮实现。

添加了一个checkBox按钮。当它状态转换时就更换 lineEdit 的 EchoMode。

发现点击之后就能够看到密码了。

Text Edit控件

QTextEdit 是一个多行的输入框,也是一个富文本和 markdown 编辑器,在文本超过范围时提供滚动条。

|---------------------------|---------------------------------------------------------------------------------------------------------------------------|
| 属性 | 说明 |
| markdown | 输入框持有的内容,支持 markdown 格式,会自动将markdown文本渲染成html |
| html | 输入框持有的内容,支持大部分html标签,包括img、table等 |
| placeHolderText | 输入框为空时提示内容 |
| readOnly | 是否是只读的 |
| undoRedoEnable | 是否开启undo/redo功能 ctrl + z 为undo(撤销) ctrl + y 为redo(恢复) |
| autoFormating | 开启自动格式化 默认为AutoNone,即不开启格式化 AutoBulletList :自动创建项目列表,如 在最左侧列输入 * 号或者在现有表项中按 enter 键 AutoAll:启动所有格式化,不过只有 AutoBulletList |
| tabstopWidth | 按下缩进占多少空间 |
| overwriteMode | 是否开启覆写(即无法从中间开始输入,只能覆盖) |
| acceptRichText | 是否接受富文本内容 |
| verticalScrollBarPolicy | 垂直方向滚动条出现的策略 Qt::ScrollBarAsNeeded :默认值, 根据需要出现 Qt::ScrollBarAlwaysOff: 总是关闭滚动条 Qt::ScrollBarAlwaysOn:总是显示滚动条 |
| horizontalScrollBarPolicy | 水平方向滚动条出现的策略 Qt::ScrollBarAsNeeded :默认值, 根据需要出现 Qt::ScrollBarAlwaysOff: 总是关闭滚动条 Qt::ScrollBarAlwaysOn:总是显示滚动条 |

核心信号

|-------------------------|---------------|
| 信号 | 说明 |
| textChanged() | 文本内容改变时触发 |
| selectionChanged() | 选中范围改变时触发 |
| cursorPositionChanged() | 光标移动时触发 |
| undoAvailable(bool) | 进行undo操作时触发 |
| redoAvailiable(bool) | 进行redo操作时触发 |
| copyAvailiable(bool) | 文本被选中/取消选中时触发 |

我们可以测试一下各种信号。

为 redo、undo、copy 和各种光标变化、文本变化添加槽函数。

能够发现在输入的时候,redoavailiable 是false,而 undoavailiable则是 true。

而当按下 ctrl + z 进行undo操作后,undoavailiable 则是false、redoavailiable则是true。

而按下 ctrl + y 进行 redo 操作后, redoavailiable 则变成了false、undoavailiable 则是true。

这说明 undo 操作和 redo 操作是相互依赖的。

而如果选中了一批内容 copyavailiable 就成true,没有选中就成false了。

Combo Box控件

核心属性

|--------------|----------------------------------------------|
| 属性 | 说明 |
| currentText | 当前选中的文本 |
| currentIndex | 当前选中的条目下标 从0开始计算,如果没有选中,则为-1 |
| editable | 是否允许修改 为true时,该控件类似 QLineEdit,也可以设置validator |
| iconSize | 下拉框图标(小三角)的大小 |
| maxCount | 最多允许有多少个条目 |

核心方法

|--------------------------|-----------------------------|
| 方法 | 说明 |
| addItem(const QString&) | 添加一个条目 |
| currentIndex() | 获取当前条目的下标 从0开始计算,如果没有选中则为-1 |
| currentText() | 获取当前条目的文本内容 |

核心信号

|---------------------------------------------------------------------|--------------------------------------------------|
| 信号 | 说明 |
| activated(int) activated(const QString& text) | 当用户选择了一个选项时发出 即用户点开下拉框,并且鼠标滑过某个选项 但是没有做出选中时发出 |
| currentIndexChanged(int) currentIndexChanged(const QString & text) | 当前选项改变时发出. 此时⽤⼾已经明确的选择了⼀个选项 ⽤⼾操作或者通过程序操作都会触发这个信号 |
| editTextChanged(const QString& text) | 编辑框中的文本改变时发出 editable为true时有效 |

我们先来使用一下 Combo box 的简单使用。

在Qt Designer 中添加一个 combobox

通过 addItem 方法添加几个条目。

这样就能够通过 combo box 右侧的下拉键来自由选择添加的条目。

不过一般这些条目都是从文件或者网络中获取。

这里我们创建一个 combo.txt 的文件。

然后通过文件流获取,由于获取的是string,而非 QString,因而在添加条目时需要通过函数转化一下

能够看到确实如此。

Spin Box控件

SpinBox是一个微调框,根据内容不同有 QSpinBox 和 QDoubleSpinBox 两个控件,不过两个使用方式都一样,不过一个是用于整型,一个是用于小数形式的。

由于二者的核心属性和信号都一样,因此这里只阐述 QSpinBox 控件。

SpinBox 控件不可输入负数

|--------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 属性 | 说明 |
| value | 当前存储的数值 |
| singleStep | 每次加减变化的数值 |
| displayInteger | 采用的进制形式,为10则表示十进制 |
| minimum | 最小值 |
| maximum | 最大值 |
| suffix | 后缀 |
| prefix | 前缀 |
| wrapping | 是否能够换行 |
| frame | 是否带边框 |
| alignment | 文字对齐方式 |
| readOnly | 是否只读 |
| buttonSymbol | 按钮图标 * UpDownArrows 箭头形式 * PlusMinus 加减号形式 * NoButtons 没有按钮 |
| accerlerated | 按下按钮时是否为快速调整模式 |
| correctionMode | 输入有误时如何修正 * QAbstractSpinBox::CorrectToPreviousValue : 输入一个无效值,自动变为上一个有效值。例如初始值为1,如果输入一个负数,自动变成1 * QAbstractSpinBox::CorrectToNearestValue:输入一个无效值,自动变为最接近的有效值,例如输入一个负数,自动变成0 |
| keyboardTrack | 是否开启键盘追踪 * true : 输入数字就会发出valueChanged() 和 textChanged() 信号 * false:按下回车才会触发 valueChanged() 和 textChanged() 信号 |

核心信号

|----------------|---------------------------------------|
| 信号 | 说明 |
| valueChanged() | SpinBox 的文本修改时触发 参数 int ,表示当前数值 |
| textChanged() | SpinBox 的文本修改时触发 参数QString,表示当前的前缀和后缀 |

我们随便整一个 SpinBox

设置它的 keyboardTrack 属性为true,即数字改变发送 valueChanged 信号,并通过槽函数输出

我们发现即便我们还在输入中,也会收到 valueChanged 信号。如果设置 keyboardTrack 属性为 false 则只有在按下 Enter 的时候发出信号。

Date Edit & Time Edit控件

有三种微调框

  • QDateEdit
  • QTimeEdit
  • QDateTimerEdit

这三种都类似,只了解一种即可。

我们来了解 QDateTimeEdit 控件。

|-----------------|----------------------------------------------------------------------------------------|
| 属性 | 说明 |
| dateTime | 时间日期的值,如 2025/1/1 0:00:00 |
| date | 单纯日期的值 |
| time | 单纯时间的值 |
| displayFormat | 时间日期格式,如 y/m/d h:m:s * y : 年 * m :月 * d : 日 * h:时 * m:分 * s : 秒 不同的语言中有不同的区别,使用的时候再去查找 |
| minimunDateTime | 最小日期时间 |
| maximumDateTime | 最大日期时间 |
| timeSpec | * Qt::LocalTime: 显示本地时间 * Qt::UTC :显示世界时间 * Qt::offsetFromUTC : 显示相对于世界时间的时差 |

核心信号

|----------------------------|-------------|
| 信号 | 说明 |
| timeChanged(QTime) | 时间改变时触发 |
| dateChanged(QDate) | 日期改变时触发 |
| dateTimeChanged(QDateTime) | 时间日期任意改变时触发 |

我们可以简单的做出一个日期计算器。

设置两个 QdateTimeEdit ,分别是 old 和 new.

然后给 pushbutton 设置一个槽函数。

通过 QDateTime 对象获取到两个微调框的时间。

然后通过 daysTo 函数获取 从 new 到 old 的天数,secsTo 函数获取从new 到 old 的秒数,再通过 /3600 后获取有多少个小时,再 %24 来得到多少个小时,最后输出在 label 上。

发现确实成功获得数据了!

Dial 控件

QDial 表示一个旋钮。

绘画软件中有一个重要的部分------调色板,它能够以旋钮的方式来调出画家想要的颜色,QDial 就是类似的旋钮。

|----------------|------------------------------|
| 属性 | 说明 |
| value | 持有的数值 |
| minimum | 最小的数值 |
| maximum | 最大的数值 |
| singleStep | 按下方向键的时候改变的步长 |
| pageStep | 按下 pageUp/pageDown 的时候改变的步长 |
| sliderPosition | 界面上旋钮显示的初识位置 |
| tracking | 外观是否会跟踪数值变化 默认为 true,一般不需要修改 |
| wrapping | 是否允许循环调整 即超过最大值是否会回到最小值 |
| notchesVisible | 是否显示刻度线 |
| notchTarget | 刻度线之间的相对位置 数字越大,刻度线越稀疏 |

核心信号

|-----------------------|---------|
| 信号 | 说明 |
| valueChanged(int) | 数值改变时触发 |
| rangeChanged(int,int) | 范围变化时触发 |

我们设置一个 dial 和 一个 label。

设置好 dial 的初始值,可以循环。

给 dial 控件的 valueChanged 信号设置槽函数。

可以看到确实成功了。

Slider控件

QSlider 是一个滑动条。

由于 QDial 和 QSlider 都继承自QAbstractSlider,因此用法其实类似。

|--------------------|-----------------------------|
| 属性 | 说明 |
| value | 当前的数值 |
| minimum | 最小值 |
| maximum | 最大值 |
| singleStep | 按下方向键修改的步长 |
| pageStep | 按下 pageUp/pageDown 的时候改变的步长 |
| sliderPosition | 滑动条显示的初识位置 |
| tracking | 外观是否跟随数值变化 默认为true |
| orientation | 滑动条的方向是水平还是垂直 |
| invertedAppearance | 是否需要翻转滑动条的方向 |
| tickPosition | 刻度的位置 |
| tickInterval | 刻度的密集程度 |

核心信号

|-----------------------|---------|
| 信号 | 说明 |
| valueChanged(int) | 数值改变时触发 |
| rangeChanged(int,int) | 范围变化时触发 |

我们设置两个 Slider 控件,分别是垂直的和水平的。

设置好初始值,由于verticalSlider(垂直滑动条) 是上大下小,因此设置翻转。

然后设置它们的槽函数。

水平的修改窗口的 width,垂直修改窗口的 height。

能够看到确实修改了窗口的大小。

一般Slider控件默认可以通过 pageDown/pageUp 或者 方向键控制。

选中某个 Slider 控件即可通过这些按键来控制Slider控件。

我们也可以自定义快捷键。

这里定义了 "-" 和 "=" 两个快捷键,并绑定它们的槽函数。

这里只修改水平滑动条的数值。

可以发现确实成功绑定了新的快捷键。

多元素控件

在QT中提供一些多元素控件。

  • QListWidget
  • QListView
  • QTableWidget
  • QTableView
  • QTreeWidget
  • QTreeView

xxWidget 都是 xxView 的子类,它们之间的区别是 xxView 是底层一点的实现,xxWidget 则是对 xxView 进行了封装。

xxView 是基于 MVC 设计的控件,它属于 MVC 机制中的 V,即视图,它只提供一个视图,而保存数据、以及数据和视图之间的交互不由它管,需要用户自己定义。

xxWidget 则是基于 xxView 的基础上,进行了封装,它内部有数据和控制器,不用用户自己定义,直接往 xxWidget 内部输入数据即可。

就比如用户使用 xxView 的时候,需要创建一个Model 对象(如QStandardModel) ,并且将Model对象和View对象绑定起来,这样修改Model对象的数据会影响View的显示,修改View的显示也会影响到Model的数据。

不过Widget对象则直接使用即可。

ListWidget控件

QListWidet是一个列表,其内部的选项都能选中。

|-------------------|------------|
| 属性 | 说明 |
| currentRow | 当前选中的行号 |
| count | 一共有多少行 |
| sortingEnabled | 是否允许倒序 |
| isWrapping | 是否允许换行 |
| itemAlignment | 元素对齐方式 |
| selectRectVisible | 被选中的元素是否可见 |
| spancing | 元素之间的间隔 |

核心方法

|--------------------------------------------------------------------------------------|------------|
| 方法 | 说明 |
| addItem(const QString& label) addItem(QListWidgetItem* item) | 列表中添加元素 |
| currentItem() | 返回当前选中的元素 |
| setCurrentItem(QListWidgetItem* item) | 设置选中的元素 |
| setCurrentRow(int row) | 设置选中第几行的元素 |
| insertItem(const QString& label,int row) insertItem(QListWidgetItem* item,int row) | 在指定的行号插入元素 |
| item(int row) | 返回第row行的元素 |
| takeItem(int row) | 删除指定行的元素 |

核心信号

|----------------------------------------------------------------------|------------------------------|
| 信号 | 说明 |
| currentItemChanged(QListWidgetItem* current, QListWidgetItem* old) | 选中不同元素时触发,参数是当前选中的元素和之前选中的元素 |
| currentRowChanged(int) | 选中不同的元素时触发,返回当前选中的行号 |
| itemClicked(QListWidgetItem* item) | 点击某个元素触发 |
| itemDoubleClicked(QListWidgetItem* item) | 双击某个元素触发 |
| itemEntered(QListWidgetItem* item) | 鼠标进入元素时触发 |

在上述的介绍中,涉及一个关键的类------QListWidgetItem。

它是QListWidget 中的元素,本质就是一个文本+图标,它有以下方法

|------------------|----------|
| 方法 | 说明 |
| setFont | 设置字体 |
| setIcon | 设置图标 |
| setHidden | 设置隐藏 |
| setSizeHint | 设置尺寸 |
| setSelected | 设置是否选中 |
| setText | 设置文本 |
| setTextAlignment | 设置文本对齐方式 |

接下来我们使用下ListWidget控件。

拖拽一个 ListView,右键后选择变型为,变成ListWidget。

然后添加两个按钮和一个lineEdit。

两个按钮的槽函数如下设置。

发现确实能够新增或者删除元素。

TableWidget控件

该控件是一个表格,有多行,每一行有多列,每一个单元格都是一个 QTableWidgetItem 对象。

核心方法

|----------------------------------------------------|------------------------------|
| 方法 | 说明 |
| item(int row,int col) | 获取对应行、列的 QTableWidgetIten 对象 |
| setItem(int row,int col,QTableWidget*) | 设置对应行、列为对应的元素 |
| currentItem() | 返回被选中的元素 |
| currentRow() | 返回被选中的元素是第几行 |
| currentColumn() | 返回被选中的元素是第几列 |
| row(QTableWidgetItem*) | 获取指定 item 是第几行 |
| column(QTableWidgetItem* ) | 获取指定的 item 是第几列 |
| rowCount() | 获取行数 |
| columnCount() | 获取列数 |
| insertRow(int row) | 在第 row 行插入新行 |
| insertColumn(int column) | 在第 column 列插入新列 |
| removeRow(int row) | 删除第 row 行 |
| removeColumn(int column) | 删除第 col 列 |
| setHorizontalHeaderItem(int column,QTableWidget*) | 设定指定列的表头 |
| setVerticalHeaderItem(int row,QTableWidget*) | 设定指定行的表头 |

该控件和 QTableWidgetItem 类关系密切,需要了解以下这个类。

QTableWidgetItem 类的核心信号

|---------------------------------------------------------------------------|------------|
| 信号 | 说明 |
| cellClicked(int row,int column) | 点击单元格触发 |
| cellDobleClicked(int row,int column) | 双击单元格触发 |
| cellEntered(int row,int column) | 鼠标进入单元格时触发 |
| currentCellChanged(int row,int column,int previousROw,int previousColumn) | 选中不同单元格时触发 |

QTableWidgetItem 类的核心方法

|------------------------------|---------|
| 方法 | 说明 |
| row() | 返回当前的行号 |
| column() | 放回当前的列号 |
| setText(const QString& str) | 设置文本 |
| setTextAlignment(int) | 设置文本对齐 |
| setIcon(const QIcon& icon) | 设置图标 |
| setSelected(bool) | 设置被选中 |
| setSizeHints(const QSize&) | 设置尺寸 |
| setFont(const QFont&) | 设置字体 |

咱们设置一个 QTableWidget 控件,和四个按钮,以及一个 lienEdit。

四个按钮的槽函数如下。

可以看到确实成功的添加行、列、删除行、列了。

TreeWidget控件

QTreeWidget 是一个树形控价,类似文件系统一样,其中每一个元素都是QTreeWidgetItem。

每一个 QTreeWidgetItem 可以包含多个文本和图标,一个文本或者图标算一列。

实际上 TreeWidget 更像一个 伪 ListWidget,它并不像一般的二叉树有根节点,而是从根节点的下一层开始计算的。

因此如果直接调用 QTreeWidget 的函数实际上都是针对顶层节点进行操作。

如果想在顶层节点下添加节点就需要先获取顶层节点然后再对顶层节点添加子节点。

接下来了解一下QTreeWidget 的函数吧。

核心方法

|---------------------------------------------|------------------------|
| 方法 | 说明 |
| clear | 清空所有节点 |
| addTopLevelItem(QTreeWidgetItem* item) | 新增顶层节点 |
| topLevelItem(int index) | 获取下标为index 的顶层节点 |
| topLevelItemCount() | 获取顶层节点的个数 |
| indexOfTopLevelItem(QTreeWidgetItem* item) | 获取指定顶层节点的下标 |
| takeTopLevelItem(int index) | 删除指定下标的顶层节点 并且返回被删除的元素 |
| currentItem() | 获取当前选中的节点 |
| setCurrentItem(QTreeWidget* item) | 选中指定节点 |
| setExpanded(bool) | 展开/关闭节点 |
| setHeaderLabel(const QString& text) | 设定 TreeWidget 的名称 |

核心信号

|---------------------------------------------------------------------|-----------|
| 信号 | 说明 |
| currentItemChanged(QTreeWidgetItem* current,QTreeWidgetItem* old) | 切换选中元素时触发 |
| itemClicked(QTreeWidgetItem* item,int col) | 点击元素时触发 |
| itemDoubleClicked(QTreeWidgetItem* item,int col) | 双击元素时触发 |
| itemEntered(QTreeWidgetItem* item,int col) | 鼠标进入时触发 |
| itemExpanded(QTreeWidgetItem* item) | 元素展开时触发 |
| itemCollaspend(QTreeWidgetItem* item) | 元素折叠时触发 |

QTreeWidgetItem 属性

|---------------|--------|
| 属性 | 方法 |
| text | 持有的文本 |
| textAlignment | 文本对齐方式 |
| icon | 持有的图标 |
| font | 文本字体 |
| hidden | 是否隐藏 |
| disabled | 是否禁用 |
| expand | 是否展开 |
| sizeHint | 尺寸大小 |
| selected | 是否选中 |

QTreeWidgetItem 方法

|-------------------------------------|----------|
| 方法 | 说明 |
| addChild(QTreeWidgetItem* item) | 新增子节点 |
| childCount() | 获取子节点的个数 |
| child(int index) | 获取指定的子节点 |
| takeChild(int index) | 删除指定的子节点 |
| removeChild(QTreeWidgetItem* item) | 删除指定的子节点 |
| parent() | 获取对应的父节点 |

了解了控件和元素的属性方法后,来试着直接使用一下。

选择QTreeWidget、LineEdit、pushButton三个控件

全部设置好。

按钮的槽函数如下。

然后就能够成功添加节点或者顶层节点了。

容器类控件

容器类控件就是能够包含多个控件的控件,在容器类控件下的控件,他们的父节点应该是对应的容器类节点。

GroupBox控件

该控件是一个带有标题的分组框,它没有什么实际作用,只是用来界面美化的。

|-----------|------------------------------|
| 属性 | 说明 |
| title | 分组框的标题 |
| alignment | 分组框内部内容的对齐方式 |
| flat | 是否是扁平模式 |
| checkable | 是否可以选择 为 ture 时,前方多出一个可勾选的部分 |
| checked | 描述分组框的选择状态(checkable必须为true) |

有一点需要注意,分组框内部的控件的父节点是分组框本身,而非窗口。

Tab Widget 控件

QTabWidget 是一个标签页。

可以设置多页,然后每一页可以添加不同的Widget,通过页切换来选择不同的widget。

|-------------------|---------------------------------------------------|
| 属性 | 说明 |
| tabPosition | 标签页所在位置 * North 上方 * South 下方 * West 左侧 * East 右侧 |
| currentIndex | 当前选中了第几个标签页 |
| currentTabText | 当前选中标签的文本 |
| currentTabName | 当前选中标签的名字 |
| currentTabIcon | 当前选中标签的图标 |
| currentTabToolTip | 当前选中标签的提示信息 |
| tabsCloseable | 标签页是否能够关闭 |
| movable | 标签是否可以移动 |

核心信号

|--------------------------|---------------------------|
| 信号 | 说明 |
| currentChanged(int) | 标签页切换时触发,参数为被点击的标签页号 |
| tabBarClicked(int) | 点击选项卡的标签条时触发,参数为被点击的选项卡编号 |
| tabBarDoubleClicked(int) | 双击选项卡的标签条时触发,参数为被点击的选项卡编号 |
| tabCloseRequest(int) | 在标签页关闭时触发,参数为被关闭的选项卡编号 |

有一点需要注意,Tab Widget 每一页都是一个 QWidget,点击标签页即可切换。

添加标签页也是新增一个 QWidget ,而这一页中的所有控件的父节点都应该是这个QWidget。

咱们添加两个按钮和一个 TabWidget 控件。

可以看出确实成功了。

布局管理器

在Qt Designer 上进行的控件布局,一般都是绝对定位的,但是当控件很多的时候,就会很不方便,因此Qt 引入了 布局管理器(layout) 机制来解决上述问题。

垂直布局

QVBoxLayout,表示垂直布局管理器。

|--------------------|-----------|
| 属性 | 说明 |
| layoutLeftMargin | 左侧边距 |
| layoutRightMargin | 右侧边距 |
| layoutTopMargin | 上方边距 |
| layoutButtonMargin | 下方边距 |
| layoutSpacing | 相邻元素之间的边距 |

通过代码方式需要通过 addWidget 方法将控件添加到 管理器中。

有一点需要注意,在创建项目时,需要采用 QWidget,而非QMainWindow。

这是因为 QMainWindow 默认自带一个 layout ,而一个Widget中只能存在一个 layout,因此不能使用 QMainWindow。

可以看到,三个按钮垂直布局,并且可以随着窗口变化而变化。

在刚刚已经讲过,一个Widget只能有一个 layout,不过在QtDesigner 中我们能够创建多个布局管理器。

但是这样的方式创建的layout,不会随着窗口变化而变化。

这是因为QTDesigner在创建layout时会自动创建一个QWidget。

在xml形式的ui文件中可以看到创建的QWidget,而这也是layout不会随着窗口变化的原因。

水平布局

使用QHBoxLayout表示水平布局管理器。

|--------------------|-----------|
| 属性 | 说明 |
| layoutLeftMargin | 左侧边距 |
| layoutRightMargin | 右侧边距 |
| layoutTopMargin | 上方边距 |
| layoutButtonMargin | 下方边距 |
| layoutSpacing | 相邻元素之间的边距 |

用法和特性跟垂直布局管理器一样。

而垂直布局和水平布局二者可以互相嵌套。

二者互相嵌套就能够做出很复杂的界面了。

网格布局

QGridLayout,表明网格布局管理器。

|-------------------------|----------|
| 属性 | 说明 |
| layoutLeftMargin | 左侧边距 |
| layoutRightMargin | 右侧边距 |
| layoutTopMargin | 上方边距 |
| layoutButtonMargin | 下方边距 |
| layoutHorizontalSpacing | 相邻元素水平间距 |
| layoutVerticalSpacing | 相邻元素垂直间距 |
| layoutRowStretch | 行方向拉伸系数 |
| layoutColumnStretch | 列方向拉伸系数 |

正如名字所说,该布局管理器是以网格方式排列的,因此它在 addWidget 时需要指定列和行。

如图所示。

如果我们将Widget全部放一行或者全部放一列,就会得到类似 水平布局或者垂直布局的结果。

不过也要注意,如果说设置了一个很大的值,但是这个值和上个值之间没有其他元素,那么中间也不会腾出其他控件。

比如之前只有3行,但是你如果添加一个在第10行的元素,它也是直接在第三行元素下面。

我们还可以设置每个按钮的拉伸系数。

这里设置第一列的拉伸系数为1,第二列不拉伸,第三列拉伸系数为3.

能够得到如下界面。

当然,我们也可以设置垂直的拉伸系数,不过有一个问题,那就是按钮的高度都是固定 的,因此需要设置按钮的垂直方向的 sizePolicy 为QSizePolicy::Expanding

QPushButton的尺寸策略

  • QSizePolicy::Ignored : 忽略控件尺寸
  • QSizePolicy::Minimum : 最小的尺寸为固定值,布局不会超过该值
  • QSizePolicy::Maximum : 最大的尺寸为固定值,布局不会超过该值
  • QSizePolicy::Prefered : 理想的尺寸为固定值,布局尽量接近该值
  • QSizePolicy::Expanding : 控件的尺寸随着空间调整,尽可能占据空间
  • QSizePolicy::Shrinking : 控件的尺寸随着空间调整,尽可能缩小

这样才能看到垂直拉伸的效果。

表单布局

QFormLayout,是QGridLayout的一种特殊情况,专门用来实现两列表单的布局。

可以看到得到如下界面。

Spacer

有时候需要在空间之间添加空白,这时候就需要 QSpacerItem 表示 。

|--------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 属性 | 说明 |
| width | 宽度 |
| height | 高度 |
| hData | ⽔平⽅向的 sizePolicy • QSizePolicy::Ignored : 忽略控件的尺⼨,不对布局产⽣影响。 • QSizePolicy::Minimum : 控件的最⼩尺⼨为固定值,布局时不会超过该值。 • QSizePolicy::Maximum : 控件的最⼤尺⼨为固定值,布局时不会⼩于该值。 • QSizePolicy::Preferred : 控件的理想尺⼨为固定值,布局时会尽量接近该值。 • QSizePolicy::Expanding : 控件的尺⼨可以根据空间调整,尽可能占据更多空间。 • QSizePolicy::Shrinking : 控件的尺⼨可以根据空间调整,尽可能缩⼩以适应空间。 |
| vData | 垂直方向的sizePolicy |

可以看到按钮之间有间距。

也可以在QtDesigner中添加。

总结

目前我们了解了QT项目中大部分常用的控件,以及其属性、信号和方法,也算是正式入门Qt了。

不过本文并没有将所有的方法都写出来,不过实际上都挺简单的,各位使用到不熟的控件可以自行搜索用法。

相关推荐
远望清一色3 分钟前
基于MATLAB的实现垃圾分类Matlab源码
开发语言·matlab
confiself13 分钟前
大模型系列——LLAMA-O1 复刻代码解读
java·开发语言
XiaoLeisj24 分钟前
【JavaEE初阶 — 多线程】Thread类的方法&线程生命周期
java·开发语言·java-ee
杜杜的man28 分钟前
【go从零单排】go中的结构体struct和method
开发语言·后端·golang
幼儿园老大*29 分钟前
走进 Go 语言基础语法
开发语言·后端·学习·golang·go
半桶水专家30 分钟前
go语言中package详解
开发语言·golang·xcode
llllinuuu30 分钟前
Go语言结构体、方法与接口
开发语言·后端·golang
cookies_s_s31 分钟前
Golang--协程和管道
开发语言·后端·golang
王大锤439133 分钟前
golang通用后台管理系统07(后台与若依前端对接)
开发语言·前端·golang
为什么这亚子33 分钟前
九、Go语言快速入门之map
运维·开发语言·后端·算法·云原生·golang·云计算