本篇章主要来自《Qt 5.9 C++开发指南》,适用于想系统学习Qt的同学。
第一章:认识Qt
问题:
1.microsoft visual studio 是什么?要与msvc编译器配套。
术语
minGW是Windows平台上使用的GNU工具集导入库的集合。
msvc是用于Windows平台上的编译器。
IDE:综合开发环境
项目组成
- qt creator 是开发qt程序的IDE
- assistant是一个独立的查看qt帮助文件的程序,集成在了qt creator中。
- designer是一个独立的进行窗口、对话框的界面可视化设计的程序。也集成在了qt creator中。在qt designer中编辑或创建界面文件时就可以自动打开界面设计。
- linguist是一个编辑语言资源文件的程序,在开发多语言界面的应用程序时会用到。
在visual studio中使用qt
visual studio中使用qt,需要安装插件。
第二章:GUI应用程序设计基础
问题:
.ui文件 是xml格式,qss文件也是xml?
qt里面的action编辑器是干什么用的。
类构造函数 explicit的作用。
setupui为什么要传入指针?
connect链接方式,带参数
UI组成
UI属性编辑器
属性和值 Property和value
qapplication.exec 开启消息循环和事件处理。
ui_widget.h是对widget.ui文件编译后生成的一个文件。
ui_widget.h内容
setupui函数
1.创建各个组件,并设置其属性:大小,位置,字体
2.调用retranslateui,设置组件的文字内容属性,如标签的文字,按钮的文字,窗口的标题
3.设置信号与槽的关联。
4.定义一个widget,继承生成的ui_widget类,头文件放在cpp中,不会互相干扰。
objectname,在窗体上创建的组件的实例名称,界面上的每个组件都需要拥有一个唯一的名称。对需要访问的修改名称,如按钮,编辑框。对不需要访问的不用修改名称,如布局,frame,groupbox。
窗体的objectname就是窗体的类名称,在ui设计器里面不要修改它。
界面组件布局
布局就是界面上组件的排列方式,使用布局可以使组件有规则的分布,并且随着窗体大小变化自动的调整大小和相对位置,。
布局的时候最后还需要为整个窗体设定一个总的布,这样在窗体大小变化的时候,内部的各个组件才会变化。
信号与槽
- 信号和槽都可以看作特殊的函数。
- 当一个信号发射时与其关联的槽函数通常立即执行,但信号所关联的所有函数执行完毕后,才会执行发射信号后面的代码。
- connectslotsbyname,会将ui里面的信号和槽关联起来。槽函数需是自动生成的。
- 自定义的槽函数需要自己调用connect接口连接。
UI设计
-
代码化ui:setlayout 设置为主窗口的布局。
-
混合ui
1)原因:在ui交互界面,spinbox,combobox无法添加到工具栏,label和progressbar无法加到状态栏,需要用代码添加的方式添加。
2)策略: 先将所有在ui设计器里可设计的静态ui元素实现,其他的用代码实现。
资源文件
.qrc,资源文件,存储图标和图片文件。
action
- 处理qaction的trigger信号的槽函数。
- 可设置文字,图标,tooltip。
- 在UI里面可以直接拖动到工具栏和菜单栏。
- add seperator。
- action的enabled和checked属性更新。同步更新。
工具栏
工具栏的toolbuttonstyle属性。
- 缺省是qt:toolbuttonicononly
其他:
qt:toolbuttontextbesideicon,文字显示在按钮旁边。
qt:toolbuttontextonly,只显示文字
qt:toolbuttontextundericon,文字显示在按钮下方。
应用程序图标
为应用程序设计图标
1.图标需是.ico。
2.项目工程文件 RC_ICONS = AppIcon.ico .pro是项目的管理文件。
第三章:Qt类库概述
问题:
动态投射有啥用啊
qvariant是什么类型,为什么可以支持其他类型。
概述
- Qt并不是一个编程语言,它实质上是一个跨平台的C++开发类库,是用标准C++编写的类库,它为开发GUI应用程序和非GUI应用程序提供了各种类。
- Qt的元对象编辑器(Moc)是一个预处理器,在源程序被编译前先将这些Qt特性的程序转换为标准C++兼容的格式,然后再由C++编译器进行编译。所以要添加一个Q_OBJECT宏。
- Qt Core模块是Qt类的核心,所有其他模块都依赖与此模块,使用qmake来构建项目,这个模块会自动加入。
- Qt为C++语言增加的特性就是在Qt Core模块里实现的,这些拓展特性由Qt的元对象系统实现,包括信号与槽机制、属性系统、动态类型转换等。
元对象系统
- 定义:元对象,意思是描述另一个对象结构的对象。比如获取一个对象有多少成员函数,有哪些属性。用到的是QMetaObject这个类。
- Qt的元对象系统提供了对象之间通信的信号与槽机制,运行时类型检查和动态属性系统。
- 组成,元对象系统由以下三个基础组成。
1)QObject类是所有需要使用元对象系统的类的基类。
2)在一个类的private部分声明Q_OBJECT宏,使得该类可以使用元对象对象的特性,比如动态属性,信号和槽。
3)元对象编译器(moc)为每个QObject的子类自动生成必要的代码来实现元对象特性。 - 其他功能
1)QObject::metaObject()函数返回类关联的元对象。
QObject *obj = new QPushButton;
obj->metaObject()->className (); //返回"QPushButton"
- QObject::inherits(const char *className) 函数判断一个对象实例是否是名称为 className 的类或 QObject 的子类的实例。例如:
QTimer *timer = new QTimer; // QTimer 是 QObject 的子类
timer->inherits ("QTimer"); // 返回 true
timer->inherits ("QObject"); // 返回 true
timer->inherits ("QAbstractButton");//返回 false,不是 QAbstractButton 的子类
3)QObject::tr() 和 QObject::trUtf8() 函数可翻译字符串,用于多语言界面设计,后续章会专门介绍多语言界面设计。
4)QObject::setProperty() 和 QObject::property() 函数用于通过属性名称动态设置和获取属性值。
5)对于 QObject 及其子类,还可以使用 qobject_cast() 函数进行动态投射.。投射失败,返回null.
属性系统
- Qt提供一个Q_PROPERTY()宏定义属性。在 QObject 的子类中,用宏 Q_PROPERTY() 定义属性,其使用格式如下:
Q_PROPERTY(type name
(READ getFunction [WRITE setFunction] |
MEMBER memberName [(READ getFunction | WRITE setFunction)])
[RESET resetFunction]
[NOTIFY notifySignal]
[REVISION int]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[CONSTANT]
[FINAL])
- Type:属性的类型
- Name:属性的名称
- READ getFunction:属性的访问函数
- WRITE setFunction:属性的设置函数
- RESET resetFunction:可选,用于指定一个设置属性缺省值的函数
- NOTIFY notifySignal:,可选,用于属性发生变化的地方发射的notifySignal信号
- DESIGNABLE bool:属性在GUI设计器Qt Designer中是否可见,默认为true
- CONSTANT:标识属性的值是常量,值为常量的属性没有 WRITE和NOTIFY关键字。
- FINAL:标识属性不会被被子类重载。
1)动态属性。QObject::setProperty() 和 QObject::property() 函数用于通过属性名称动态设置和获取属性值。
2)Q_CLASSINFO()
可以为类的元对象定义“名称——值”;用 Q_CLASSINFO() 宏定义附加类信息后,可以通过元对象的一些函数获取类的附加信息,如 classlnfo(int) 获取某个附加信息,函数原型定义如下:
QMetaClassInfo QMetaObject::classInfo(int index) const
信号与槽
QMetaObject::Connection QObject::connect(
const QObject *sender,
const char *signal,
const QObject *receiver,
const char *method,
多参数时用QOverload重载
connect(this,QOverload<QString>::of(&MyButton::hungury),this,QOverload<QString>::of(&MyButton::eat));
- 连接方式Qt::ConnectionType
1)Qt::AutoConnection。自动连接:(默认值)如果信号在接收者所依附的线程内发射,则等同于直接连接。如果发射信号的线程和接受者所依附的线程不同,则等同于队列连接。
2)Qt::DirectConnection。直接连接:当信号发射时,槽函数将直接被调用。无论槽函数所属对象在哪个线程,槽函数都在发射信号的线程内执行。[这种方式不能跨线程传递消息]
3)Qt::QueuedConnection。队列连接:当控制权回到接受者所依附线程的事件循环时,槽函数被调用。槽函数在接收者所依附线程执行。[这种方式既可以在线程内传递消息,也可以跨线程传递消息]
4)Qt::BlockingQueuedConnection.与Qt::QueuedConnection类似,但是发送消息后会阻塞,直到等到关联的slot都被执行。[说明它是专门用来多线程间传递消息的,而且是阻塞的] - 使用sender()获得信号发送者。
- 自定义信号,自定义信号就是在类里面声明的一个函数,但是这个函数无需实现,只需要emit.
第四章:常用界面组件设计
疑问?
转义字符是什么东西,什么时候用到?
\
1. 字符串和输入输出
1)界面设计最常用到的组件就是QLabel和QLineEdit;
QLabel用于显示字符串,QLineEdit用于输入和显示字符串。两个函数都涉及到QString类。QString类是Qt程序中经常使用的类,用于处理字符串,可以实现字符串与数值之间的转换。
QString使用:字符串和数值的转换
- 字符串转换为整数,QString成员函数。
1)int toInt(bool *ok = Q_NULLPTR, int base = 10);
2)还支持toLong, toShort, toUInt, toULong
bool ok;
int val = str.toInt(&ok,2); //以二进制读入
-
字符串转换为浮点数
1)double toDouble(bool *ok = Q_NULLPTR);
2) float toFloat(bool *ok = Q_NULLPTR); -
浮点数转换为字符串
str = QString::number(total, 'f', 2);
str = QString::asprintf("%.2f", total);
str = str.setNum(total, 'f', 2);
str = str.sprintf("%.2f", total);
- 进制转换。
1)QString number(int n, int base = 10); base是要转换成的进制数。
2)Qstring &setNum(int n, int base = 10);
QString使用:常用功能
- 编码:QString字符串采用的是Unicode码,每一个字符是16位的QChar,而不是八位的char,所以QString处理中文字符没有问题,而且一个汉字算一个字符。
- 常用函数
1)append和prepend,在前面还是在后面加上字符串。
2) toUpper和toLower,转换为大写/小写
3)count,size,length都相同,注意汉字返回一个字符
4)trimmed和simplified.trimmed去掉首位的空格;simplified去掉首尾的空格,还会把中间连续的空格变为一个空格。
5) indexOf和lastIndexOf
- isNull和isEmpty,判断内容是否为空,用isEmpty。只有未赋值的字符串,isNull才返回true.
- contains,是否包含某个字符串,区分大小写
- endsWith和startsWith,是否以某个字符串结尾/开头
- left和right.取左边/右边多少个字符,
- section,提取字符串。
2. SpinBox的使用
- QSpinBox用于整数输入和显示,可以用于10进制数,2进制数和16进制数
- QDoubleSpinBox用于浮点数输入和显示。
- 在使用QSpInBox和QDoubleSpinBox读取和设置数值时,无需做字符串和数值之间的转换,也无需做进制之间的转换,其显示效果设置好之后就能按照效果进行显示,这对于输入数值来说是十分方便的。设置的时候用int,需要有转换为QString.
3. 其他数值输入和显示组件
4. 时间日期和定时器
- QTimeEdit,QDateEdit,QDateTimeEdit,QCalendarWidget(日历形式选择日期的组件)。
- QTimer,是以毫秒为单位的定时器。每次到达的时候会发送timeout信号。从QObject类继承来。
- 时间转换为字符串。QString QDateTime::toString(const QString &format) const。格式里面可以填写字符/汉字,会直接替换。
- 字符串转换为时间。QDateTime QDateTime::fromString(const QString &string, const QString &format).
5. QComboBox和QPlainTextEdit
QComboBox使用
主要的功能是提供一个下拉列表供选择输入。
- 添加具有用户数据的项。
void addItem(const QString &text, const QVariant &userData = QVariant())//第二个参数存储用户自定义数据
void addItem(const QIcon &icon, const QString &text, const QVariant &userData = QVariant())
如果是QStringList,可直接用addItems();
2. 列表项的访问
int currentlndex():返回当前项的序号,第一个项的序号为0。
QString currentText():返回当前项的文本。
QVariant currentData(int role = Qt::UserRole):返回当前项的关联数据,数据的缺省角色为 role = Qt::UserRole。
QString itemText(int index):返回指定索引项的文字。
QVariant itemData(int index, int role = Qt::UserRole):返回指定索引号的项的关联数据。
int count():返回项的个数。
QComboBox 组件上选择项发生变化时,会发射如下两个信号:
void currentlndexChanged(int index) //传递当前项的索引
void currentlndexChanged(const QString &text) //传递当前项的文字.
QPlainTextEdit使用
6. QListWidget和QToolButton
- QToolButton,可以用作工具栏。有文字和图标。可以与action关联起来,也可以设置下拉式菜单。setmenu();
- QToolBox。QToolBox类提供一列带标签的窗口小部件项目。
属性
currentIndex.当前分组编号,第一个分组的编号是0. currentItemText.当前分组的标题
currentItemName,当前分组的对象名称 currentItemIcon,为当前分组设置一个图标,显示在文字标题的左侧。
在ToolBox里面,可以放置任何界面组件。
- QTabWidget使用
属性
tabPosition.页标签的位置,东南西北
currentIndex.当前分组编号,第一个分组的编号是0.
currentTabText.当前分组的标题
currentTabName,当前分组的对象名称
currentTabIcon,为当前分组设置一个图标,显示在文字标题的左侧。
- QSplitter 设计???后续拓展
可以设置子部件的最小宽度。 - QListWidget设置
项的属性
selectable:项是否可以被选择,对应枚举值Qt::ItemIsSelectable.
Editable:项是否可以被编辑,对应枚举值Qt::ItemIsEditable
DragEnabled:项是否可以被拖动,对应枚举值Qt::ItemIsDragEnabled
DropEnabled:项是否可以接收拖放的项,对应枚举值Qt::ItemIsDropEnabled
UserCheckable:项是否可以被复选,若为true,项前面出现一个CheckBox,对应枚举值Qt::ItemIsUserCheckable
Enabled:项是否被使能,对应枚举值Qt::ItemIsEnabled
Tristate:是否允许check的第三种状态,若为false,则只有checked和unchecked两种状态,对应枚举值Qt::ItemIsAutoTristate。第三种状态是Qt::PartiallyChecked,部分选中。
使用setFlags设置项的属性。如
aItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable |Qt::ItemIsEnabled);
QListWidget操作
- 初始化列表。
列表框的一行是一个项,是一个QListWidgetItem类型的对象,向列表框里添加一个项,就需要创建一个QListWidgetItem类型的实例altem,然后设置aItem的一些属性,再用QListWidget::addItem函数将该altem添加到列表框里。 - 插入项
插入项使用QListWidget::insertItem(int row, QListWidgetItem *item)函数,在某一行row的前面插入一个QListWidgetItem对象。也是要先创建,然后设置属性。 - 删除当前项和清空列表
QListWidgetItem *pItem = ui->listWidget->takeItem(row);
delete pItem;
takeItem只是移除一个项,并不是删除项对象,所以还需要用delete.
要清空列表框的所有项,只需调用QListWidget::clear()即可。会自动释放内存。如果连接了信号,要先断开信号的连接,不然删除最后一项时可能导致段错误。
4. QlistWidget常用信号。
项切换的信号。
currentRowChanged(int row);
currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous);
当前项内容改变。
currentTextChanged(const QString ¤tText);
其他信号
void itemChanged(QListWidgetItem * item); //项的内容被改变发出信号.
void itemClicked(QListWidgetItem * item); //项被点击发出信号.
QToolButton与下拉式菜单
- QToolButton关联QAction.
void QToolButton::setDefaultAction(QAction *action);
调用后,QToolButtion自动获取Action的文字、图标、ToolTip等设置为按钮的相关属性。
使用Action是避免重复编写代码的一种方式。
2. 为QToolButton按钮设计下拉菜单。
QToolButton::set PopupMode(QToolButton::MenuButtonPopup)设置菜单弹出的方式。
模式1:QToolButton::MenuButtonPopup。单击小箭头会弹出下拉框,直接单击按钮会执行action,不会弹出下拉框。
模式2:QToolButton::InstantPopup。单击按钮弹出下拉框,不会执行关联的action.
- 创建右键快捷菜单
每个从QWidget继承的类都有信号customContextMenuRequested(QPoint),这个信号在鼠标右击时发射。该信号发出的条件是:widget的ContextMenuPolicy必须是Qt::CustomContextMenu
为该信号连接槽函数。
void Widget::show_menu(const QPoint pos)
{
Q_UNUSED(pos);
//设置菜单选项
QMenu *menu = new QMenu(this);
menu->addAction(pAction1);
menu->addAction(pAction2);
menu->addSeparator();
menu->addAction(pAction3);
menu->addAction(pAction4);
menu->exec(cursor::.pos ());
delete menu;
}
QMenu::exec()函数显示快捷菜单。
7. QTreeWidget和QDockWidget
QDockWidget属性设置
allowedAreas属性,设置允许停靠区域
void setAllowedAreas(Qt::DockWidgetAreas areas) .
features属性,设置停靠区组件的特性。
void setFeatures(QDockWidget::DockWidgetFeatures features) 可以设置窗口特征,默认AllDockWidgetFeatures
QDockWidget::DockWidgetClosable 可关闭
QDockWidget::DockWidgetMovable 可移动。
QDockWidget::DockWidgetFloatable 可浮动。
QDockWidget::DockWidgetVerticalTitleBar 在左侧显示一个垂直的标题栏
QDockWidget::AllDockWidgetFeatures 使用以上所有特性
QDockWidget::NoDockWidgetFeatures 无法关闭、移动和浮动dock小部件。
QTreeWidget属性设置
- UI里面,Columns页用于设计列,有多少列,列的文字、字体、前景色、背景色、文字对齐方式、图标等。
- Item列用于设计节点,可对每个节点设置属性,如文字、字体、图标等。还有flag属性,设置节点是否可选,是否可编辑、是否有CheckBox等。
QTreeWidget操作
QTreeWidget的每个节点都是一个QTreeWidgetItem,添加一个节点前需要先创建它,并做好相关设置。
void QTreeWidget::addTopLevelItem(QTreeWidgetItem * item)。 为QTreeWdiget增加顶级项目
void QTreeWidgetItem::addChild(QTreeWidgetItem * child)
为父节点增加子节点
int QTreeWidgetItem::type();返回节点的类型,即刚开始创建时传入的值。
currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); 节点改变,发送信号
删除节点
方式1:
QTreeWidgetItem * curItem;
QTreeWidgetItem * pParentItem = curItem->parent();
pParentItem->removeChild(curItem);
delete curItem;
一个节点不能移除自己,需要通过其父节点的removeChild移除,并调用delete。
方式2:
QTreeWidget::takeTopLevelItem(int index); 删除顶层节点。
遍历:
int topLevelItemCount(),返回顶层节点的个数。
QTreeWidgetItem *topLevelItem(int index); 返回序号为index的顶层节点。
childCount()
child(int index);
QLabel和QPixmap显示图片
QPixmap::load(QString &fileName),直接将一个图片加载进来。
QPixmap scaled(int width, int height);返回一个缩放后的图片的副本。
QLabel::setPixmap(const QPixmap &);函数
QDockWidget操作
单击关闭按钮时,会发送visibilityChanged(bool);信号
拖动dockWidget组件时,会发射信号topLevelChanged(bool);
8. QTableWidget的使用
基本结构
- 表格的第 1 行称为行表头,用于设置每一列的标题,第 1
列称为列表头,可以设置其标题,但一般使用缺省的标题,即为行号。行表头和列表头一般是不可编辑的。
设置表头:
- 如果只是设置行表头各列的标题,使用下面一行语句即可:
ui->tableInfo->setHorizontalHeaderLabels(headerText); - 如果需要进行更加具体的格式设置,需要为行表头的每个单元格创建一个 QTableWidgetItem 类型的变量,并进行相应设置。ui->tableInfo->setHorizontalHeaderItem(i,headerItem); //设置表头单元格的Item
- 在一个表格中,不管是表头还是工作区,每个单元格都是一个 QTableWidgetItem 对象。
QTableWidget::clearContents() 函数清除表格数据区的所有内容,但是不清除表头。
QTableWidget::rowCount() 函数返回表格数据区的行数。
创建 QTableWidgetItem 使用的构造函数的原型为:
QTableWidgetItem::QTableWidgetItem (const QString &text, int type = Type)
其中,第一个参数作为单元格的显示文字,第二个参数作为节点的类型。
QTableWidgetItem 有一些函数对单元格进行属性设置,如下:
setTextAlignment (int alignment):设置文字对齐方式
setBackground(const QBrush &brush):设置单元格背景颜色。
setForeground(const QBrush &brush):设置单元格前景色。
setIcon(const QIcon &icon):为单元格设置一个显不图标。
setFont(const QFont &font):为单元格显示文字设置字体。
setCheckState(Qt::CheckState state):设置单元格勾选状态,单元格里出现一个QCheckBox组件。
setFlags(Qt::ItemFlags flags):设置单元格的一些属性标记。
- 获得当前单元格数据
- 当鼠标在表格上单击单元格时,被选中的单元格是当前单元格。通过 QTableWidget 的 currentColumn() 和
currentRow() 可以获得当前单元格的列编号和行编号。 - 当前单元格发生切换时,会发射 currentCellChanged()
信号和 currentItemChanged() 信号,两个信号都可以利用,只是传递的参数不同。
- 插入、添加、删除行
- insertRow(int row):在行号为row的行前面插入一行,如果row等于或大于总行数,则在表格最后添加一行。insertRow()函数只是插入一个空行,不会为单元格创建QTableWidgetItem对象,需要手工为单元格创建。
- removeRow(int row):删除行号为 row 的行。
- 自动调整行高和列宽
- resizeColumnsToContents():自动调整所有列的宽度,以适应其内容。
- resizeColumnToContents(int column):自动调整列号为 co/www 的列的宽度。
- resizeRowsToContents():自动调整所有行的高度,以适应其内容。
- resizeRowToContents(int row):自动调整行号为 raw 的行的高度。
这几个函数实际上是 QTableWidget 的父类 QTableView 的函数。
- 其他属性控制
- 设置表格内容是否可编辑:QTableWidget 的 EditTriggers 属性表示是否可编辑,以及进入编辑状态的方式。界面上的"表格可编辑"复选框的槽函数代码为:
void MainWindow::on_chkBoxTabEditable_clicked(bool checked)
{ //设置编辑模式
if (checked)
//双击或获取焦点后单击,进入编辑状态
ui->tableInfo->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::SelectedClicked);
else
ui->tableInfo->setEditTriggers(QAbstractItemView::NoEditTriggers); //不允许编辑
}
- 设置行表头、列表头是否显示:horizontalHeader()获取行表头,verticalHeader()获取列表头,然后可设置其可见性。
- 间隔行底色:setAltematingRowColors() 函数可以设置表格的行是否用交替底色显示,若为交替底色,则间隔的一行会用灰色作为底色。具体底色的设置需要用 styleSheet,在后续章节会有介绍。
- 选择模式:setSelectionBehavior() 函数可以设置选择方式为单元格选择,还是行选择
第五章:Model/View结构
1. Model/View结构
简介
源数据由模型 (Model) 读取,然后在视图 (View) 组件上显示和编辑,在界面上编辑修改的数据又通过模型保存到源数据。
Model/View 结构将数据模型和用户界面分离开来,分别用不同的实现,是一种显示和编辑数据的有效结构,在处理大型数据时尤其明显。
- Data(源数据)是原始数据,如数据库的一个数据表或SQL查询结果、内存中的一个字符串列表或磁盘文件结构等
- Model(模型/数据模型)与源数据通信,并为视图组件提供数据接口。它从源数据提取需要的数据,用于视图组件进行显示和编辑
- View(视图/视图组件)是界面控件,视图从数据模型中根据一定条件(如行号、列号等)获得模型索引(一个指向数据项的引用),然后显示在界面上
- Delegate(代理)在视图与模型之间交互操作时提供临时编辑组件的功能。在标准的视图组件中,代理功能显示一个数据;当数据被编辑时,提供一个编辑器,一般是QLineEdit。
通信机制
模型、视图、代理之间使用信号与槽通信。
- 当源数据发生变化时,数据模型发射信号通知视图组件
- 当用户在界面上操作数据时,视图组件发射信号表示这些操作信息
- 在编辑数据时,代理会发射信号告知数据模型和视图组件编辑器的状态。
数据模型
所有的基于项数据(item data)的数据模型(Model)都是基于QAbstractItemModel类的。这个类定义了视图组件和代理存取数据的接口。数据无需存储在数据模型中(组合)。一下有些抽象类不能直接使用,需要由子类继承来实现一些纯虚函数。
Qt提供了一些模型类用于项数据处理,如下。如果如下不能满足要求,可以继承QAbstractItemModel、QAbstractListModel或QAbstractTableModel,生成自定义的数据模型类。
视图组件
视图组件(view)就是显示数据模型的数据的界面组件,qt提供的视图组件如下。
- QListView:用于显示单列的列表数据,适用于一维数据的操作。
- QTreeView:用于显示树状结构数据,适用于树状结构数据的操作。
- QTableView:用于显示表格状数据,适用于二维表格型数据的操作。 QColumnView:用多个 QListViews
中显示树状层次结构,树状结构的一层用一个QListViews显示。 - QHeaderView:提供行表头或列表头的视图组件,如QTabelView的行表头的列表头。 如何理解??
使用
视图组件在显示数据时,只需调用时只需调用视图类的setModel()函数,为视图组件设置一个数据模型就可以实现视图组件与数据模型之间的关联,在视图组件上的修改将自动保存到关联的数据模型里,一个数据模型可以同时在多个视图组件里显示数据。
视图相关类
Qlistwidget等称为便利类,缺乏对大型数据灵活处理的能力,适用于小型数据的显示和编辑。其为每个节点创建了一个item。
代理
- 代理就是在视图组件上为编辑数据提供编辑器,如在表格组件里编辑一个单元格时,缺省是使用一个QLineEdit编辑框。代理负责从数据模型获取相应的数据,然后显示在编辑器里,修改数据后,又负责保存到数据模型中。
- QAbstraceItemDelegate是所有代理类的基类,是一个抽象类,不能直接使用。它的一个子类QStyledItemDelege,是Qt的视图组件缺省使用的代理类。
- 对于一些自定义组件,可以继承QStyledItemDelege实现。
Model/View结构的一些概念
1. Model/View的基本结构
数据模型中存储数据的基本单元都是项(item),每个项有一个行号、列号和一个父项(parent item)。列表和表格有一个相同的顶层项(root item)。三个参数确定一个项的位置。
2. 模型索引 model index
- 为了确保数据的表示与访问数据的方式保持分离,引入了模型索引的概念。通过模型获得的每一条信息都由模型索引表示。视图和代理使用这些索引获取。
- QModelIndex表示模型索引的类,模型索引提供数据存取的一个临时指针,用于通过数据模型提取或修改数据。因为模型内部结构可能改变,所以是临时的。如果想用持久性的模型索引,则要使用QPersistentModelIndex。 ???在讲啊,不是很懂。
- 通过数据模型获取索引。
3. 行号和列号
通过行号和列号获取索引,列表和表格的顶层节点总是用QModelIndex()表示。
4. 父项
树状比较特殊。
5. 项的角色
- 在为数据模型的一个项设置数据时,可以赋予其不同项的角色的数据。
例如,数据模型类QStandardItemModel的项数据类是QStandardItem,其设置数据的函数是:
void QStandardItem::setData(const QVariant, int role = Qt::UserRole + 1);
- value是需要设置的数据,role是设置数据的角色。一个项可以有不同角色的数据,用于不同的场合
- role是Qt::ItemDataRole枚举类型,有多种值,如Qt::DisplayRole角色是在视图组件中显示的字符串Qt::ToolTipRole是鼠标提示的消息,Qt::UserRole可以自定义数据。项的标准角色是Qt::DisplayRole。
在获取一个项的数据时也需要指定角色,以获取不同角色的数据:
QVariant QStandardItem::data(int role = Qt::UserRole + 1);
为一个项的不同角色定义数据,可以告知视图组件和代理组件如何显示数据。
2. QFileSystemModel
1. QFileSystemModel类的基本功能
-
QFileSystemModel 提供了一个可用于访问本机文件系统的数据模型。
-
QFileSystemModel 和视图组件 QTreeView 结合使用,可以用目录树的形式显示本机上的文件系统,如同 Widnows
的资源管理器一样。使用 QFileSystemModel
提供的接口函数,可以创建目录、删除目录、重命名目录,可以获得文件名称、目录名称、文件大小等参数,还可以获得文件的详细信息。 -
要通过 QFileSystemModel 获得本机的文件系统,需要用 setRootPath() 函数为 QFileSystemModel
设置一个根目录,例如:
QFileSystemModel *model = new QFileSystemModel;
model->setRootPath(QDir::currentPath());
- 用于获取磁盘文件目录的数据模型类还有一个 QDirModel,QDirModel 的功能与 QFileSystemModel 类似,也可以获取目录和文件,但是 QFileSystemModel 采用单独的线程获取目录文件结构,而 QDirModel 不使用单独的线程。使用单独的线程就不会阻碍主线程,所以推荐使用 QFileSystemModel。
2. 常用API
//判断节点是不是一个目录
bool isDir(const QModelIndex &index) const
//文件路径
QString filePath(const QModelIndex &index) const
//文件名
QString fileName(const QModelIndex &index) const
//文件类型,如文件夹,txt文件等
QString type(const QModelIndex &index) const
//文件大小
qint64 size(const QModelIndex &index) const
3. QStringListModel
1. QStringListModel功能概述
- QStringListModel是用于处理字符串列表的数据模型,它可以作为QListView的数据模型,在界面上显示和编辑字符串列表。
- QStringListModel的setStringList()函数可以初始化数据模型的字符串列表的内容,stringList() 函数返回数据模型内的字符串列表,在关联的ListView组件里编辑数据后,数据都会及时更 新到数据模型内的字符串列表里。
- QStringListModel提供编辑和修改字符串列表数据的函数,如**insertRows()、removeRows()、 setData()**等,这些操作直接影响数据模型内部的字符串列表,并且修改后的数据会自动在关联的ListView组件里刷新显示。
2.QStringListModel的使用
- 添加项。insertRow(int row),在row行前插入一行。要在最后加入一行,row为当前行数即可。
- 插入项。理解下面设置插入行的文字。
void Widget::on_btnListInsert_clicked()
{
//插入一行
QModelIndex index;
index=ui->listView->currentIndex(); //当前 modelIndex
theModel->insertRow(index.row()); //在当前行的前面插入一行
theModel->setData(index,"inserted item",Qt::DisplayRole); //设置插入行显示文字
ui->listView->setCurrentIndex(index); //设置当前选中的行
}
- 删除当前项。removeRow(int row);
- 删除列表。removeRows(0, model->rowCount());
4. QStandardItemModel
1. 功能概述
- QStandardItemModel 是标准的以项数据(item data)为基础的标准数据模型类,通常与 QTableView 组合成Model/View 结构,实现通用的二维数据的管理功能。
本节介绍 QStandardltemModel 的使用,主要用到以下 3 个类:
- QStandardItemModel:基于项数据的标准数据模型,可以处理二维数据。维护一个二维的项数据数组,每个项是一个
QStandardltem 类的变量,用于存储项的数据、字体格式、对齐方式等。 - QTableView:二维数据表视图组件,有多个行和多个列,每个基本显示单元是一个单元格,通过 setModel() 函数设置一个QStandardItemModel 类的数据模型之后,一个单元格显示 QStandardItemModel 数据模型中的一个项。
- QItemSelectionModel:一个用于跟踪视图组件的单元格选择状态的类,当在 QTableView选择某个单元格,或多个单元格时,通过 QItemSelectionModel 可以获得选中的单元格的模型索引,为单元格的选择操作提供方便。
这几个类之间的关系是:QTableView 是界面视图组件,其关联的数据模型是 QStandardItem Model,关联的项选择模型是 QItemSelectionModel,QStandardItemModel 的数据管理的基本单元是 QStandardItem。
2. QStandardItemModel的使用。
初始化代码
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui (new Ui::MainWindow)
{
ui->setupUi(this);
setCentralWidget(ui->splitter);
theModel = new QStandardltemModel (2, FixedColumnCount, this) ; //数据模型
theSelection = new QItemSelectionModel (theModel) ;//选择模型
connect(theSelection,SIGNAL(currentChanged(QModelIndex,QModelIndex)), this,SLOT(on_currentChanged(QModelIndex,QModelIndex)));
ui->tableView->setModel (theModel) ; //设置数据模型
ui->tableVi.evi-> setSelectionModel(theSelection) ; //设置选择模型
ui->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);
//创建状态栏组件,代码略
}
5. 自定义代理
为某列或某个单元格设置自定义代理组件。
1. 自定义代理类的基本设计要求
Qt中有关代理的几个类的层次结构。
QStyledItemDelegate是视图组件使用的缺省的代理类,QItemDelegate也是类似功能的类。
QStyledItemDelegate和QItemDelegate区别在于,QStyledItemDelegate可以使用当前的样式表设置来绘制组件,因此建议使用QStyledItemDelegate。
还要必须实现以下几个函数
- createEditor():创建用于编辑模型数据的widget组件,如QSpinBox,QComboBox。
- setEditorData():从数据模型获取数据,供widget组件进行编辑。
- setModelData():将widget上的数据更新到数据模型。
- updateEditorGeometry():用于给widget组件设置一个合适的大小。
2. 基于QSpinBox的自定义代理类
- 自定义代理类的基本结构
class SpinBoxDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit SpinBoxDelegate(QObject *parent = nullptr);
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void setEditorData(QWidget *editor, const QModelIndex &index) const;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
- createEditor函数的实现
QWidget *SpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QSpinBox *editor = new QSpinBox(parent);
//设置无边框
editor->setFrame(false);
//设置最小值
editor->setMinimum(0);
//设置最大值
editor->setMaximum(100);
//返回
return editor;
}
创建了一个QSpinBox类型的编辑器editor, parent指向视图组件;
- setEditorData()函数的实现
void SpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
int value = index.model()->data(index,Qt::EditRole).toInt();
QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
spinBox->setValue(value);
}
setEditorData函数用于从模型获取数值,设置为编辑器的显示值。当双击一个单元格进入编辑状态时,就会自动调用此函数。
函数传递来的参数editor指向代理编辑组件,index是关联的数据单元的模型索引。
- setModelData()函数的实现
void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
spinBox->interpretText();
int value = spinBox->value();
model->setData(index,value,Qt::EditRole);
}
setModelData用于将代理编辑器上的值更新给数据模型,当用户在界面上完成编辑时会自动调用此函数。
- updateEditorGeometry()函数的实现
void SpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
updateEditorGeometry函数用于为代理组件设置一个合适的大小,函数传递的参数option的rect变量定义了单元格适合显示代理组件的大小,直接设置为此值即可。
3. 自定义代理类的使用
- 为tableview的某一列设置自定义代理组件。使用setItemDelegateForColumn。
- 为某一行设置自定义组件,使用setItemDelegateForRow。
- 为整个TableView设置自定义代理组件,使用setItemDelegate。
MVC实例:
https://blog.csdn.net/panchang199266/article/details/104849986/
第六章:对话框和多窗体设计
1. 标准对话框
1. 概述
Qt 预定义的各标准对话框的类,及其主要静态函数的功能见下表(由于输入参数一般较多,省略了函数的输入参数,只列出了函数的返回值类型)。
2. QFileDialog对话框
- 选择打开一个文件
QFileDialog::getOpenFileName(this,dlgTitle,curPath,filter);
QFileDialog::getOpenFileName() 函数需要传递 3 个字符串型参数,分别如下:
1.对话框标题,这里设置为"选择一个文件"。
2.初始化目录,打开对话框时的初始目录。
3. 文件过滤器,设置选择不同后缀的文件,可以设置多组文件,如:
QString filter="文本文件(*.txt);;图片文件(* .jpg *.gif *.png);;所有文件(*.*)";
每组文件之间用两个分号隔开,同一组内不同后缀之间用空格隔开。
QFileDialog::getOpenFileName()函数返回的是选择文件的带路径的完整文件名,如果在对话框里取消选择,则返回字符串为空。
- 选择打开多个文件
getOpenFileNames() 函数的传递参数getOpenFileName()
—样,只是返回值是一个字符串列表,列表的每一行是选择的一个文件。
- 选择已有目录
选择己有目录可调用静态函数QFileDialog::getExistingDirectory(),同样,若需要传递对话框标题和初始路径,还应传递一个选项,一般用
QFileDialog::ShowDirsOnly,表示对话框中只显示目录。静态函数 QCoreApplication::applicationDirPath()
返回应用程序可执行文件所在的目录,getExistingDirectory() 函数的返回值是选择的目录名称字符串。
- 选择保存文件名
选择一个保存文件,使用静态函数 QFileDialog::getSaveFileName(),传递的参数与
getOpenFileName() 函数相同。只是在调用 getSaveFileName()函数时,若选择的是一个己经存在的文件,会提示是否覆盖原有的文件。如果提示覆盖,会返回为选择的文件,但是并不会对文件进行实质操作,对文件的删除操作需要在选择文件之后自己编码实现。
3. QColorDialog 对话框
QColorDialog 是选择颜色对话框,选择颜色使用静态函数
QColorDialog::getColor()。下面是“选择颜色”按钮的代码,它为文本框的字体选择颜色。
void Dialog::on_btnColor_clicked()
{
QPalette pal=ui->plainTextEdit->palette(); //获取现有 palette
QColor iniColor=pal.color(QPalette::Text); //现有的文字颜色
QColor color=QColorDialog::getColor(iniColor,this,"选择颜色");
if (color.isValid()) //选择有效
{
pal.setColor(QPalette::Text,color); //palette 设置选择的颜色
ui->plainTextEdit->setPalette(pal); //设置 palette
}
}
getColor() 函数需要传递一个初始的颜色,这里是将palette提取的文本颜色作为初始颜色。getColor()
函数返回一个颜色变量,若在颜色对话框里取消选择,则返回的颜色值无效,通过 QColor::isValid() 函数来判断返回是否有效。
不是很懂。
4. QFontDialog 对话框
QFontDialog 是选择字体对话框,选择字体使用静态函数
QFontDialog::getFont()。下面是“选择字体”按钮的代码,它为文本框选择字体,字体设置的内容包括字体名称、大小、粗体、斜体等。
void Dialog::on_btnFont_clicked()
{//选择字体
QFont iniFont=ui->plainTextEdit->font(); //获取文本框的字体
bool ok=false;
QFont font=QFontDialog::getFont(&ok,iniFont); //选择字体
if (ok) //选择有效
ui->plainTextEdit->setFont(font);
}
getFont() 返回一个字体变量,但是 QFont 没有类似于 isValid() 的函数来判断有效性,所以在调用 getFont()
函数时以引用方式传递一个逻辑变量 ok,调用后通过判断 ok 是否为 true 来判断字体选择是否有效。
5. QInputDialog标准输入对话框
QInputDialog 有单行字符串输入、整数输入、浮点数输入、列表框选择输入和多行文本等多种输入方式。
效果图:
使用 QInputDialog::getItem() 可以从一个 ComboBox 组件的下拉列表中选择输入。
6. QMessageBox 消息对话框
- 简单信息提示
消息对话框 QMessageBox 用于显示提示、警告、错误等信息,或进行确认选择,由几个静态函数实现这些功能(详见表 1)。其中warning()、information()、critical() 和 about()
这几个函数的输入参数和使用方法相同,只是信息提示的图标有区别。
例如,warning() 的函数原型是:
StandardButton QMessageBox::warning(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton)
其中,parent 是对话框的父窗口,指定父窗口之后,打开对话框时,对话框将自动显示在父窗口的上方中间位置;title是对话框标题字符串;text 是对话框需要显示的信息字符串;buttons 是对话框提供的按钮,缺省只有一个 OK
按钮;defaultButton 是缺省选择的按钮,缺省表示没有选择。
StandardButton 是各种按钮的定义,如 OK、Yes、No、Cancel 等,其枚举取值是 QMessageBox::Ok、QMessageBox::Cancel、QMessageBox::Close 等。
详见 Qt 帮助文档中的StandardButton 类型的说明。
对于 warning()、information()、critical() 和 about() 这几种对话框,它们一般只有一个 OK 按钮,且无须关心对话框的返回值。所以,使用缺省的按钮设置即可。
效果图
- 确认选择对话框
QMessageBox::question() 函数用于打开一个选择对话框,提示信息,并提供 Yes、No、OK、Cancel等按钮,用户单击某个按钮返回选择,如常见的文件保存确认对话框如图 5 所示。
态函数 QMessageBox::question() 的原型如下:
StandardButton QMessageBox::question(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons = StandardButtons( Yes | No ), StandardButton defaultButton = NoButton)
question() 对话框的关键是在其中可以选择显示多个按钮,例如同时显示 Yes、No、OK 或 Cancel() 其返回结果也是一个 StandardButton 类型变量,表示哪个按钮被单击了。
效果图
2. 自定义对话框及其调用
继承自QDialog
1. QWDialogSize的创建和使用
- 对话框每次动态创建,以模态方式显示(必须关闭此对话框才可以返回主窗口),对话框关闭后获取返回值,然后删除对话框对象,是否内存。
- 这种对话框创建和调用方式比较简单,不需要从主窗口传递大量数据做初始化的对话框,调用之后可以删除内存。
实现
1. btnOK的clicked()信号与对话框的accept()槽关联,将btnCancel的clicked()信号与对话框的reject()槽关联即可,
2. QWDialogHeaders的创建和使用
1.只创建一次,以模态方式存在,关闭后只是隐藏,不删除。
2.使用与比较复杂的对话框,需要从父窗口传递大量数据做对话框初始化。
3. QWDialogLocate的创建和使用
1.以非模态方式存在。模态使用的是QDialog::exec().非模态使用QDialog::show()
2.适用于主窗口与对话框需要交互操作的情况,例如用于查找和替换的对话框。