Qt实现地址簿
示例运行效果:
该示例实现了一个按名称首字母分类展示的地址簿。可以添加、修改、删除一条信息。可以将当前地址簿以文件形式保存到本地,并且可以打开本地的地址簿。
文件目录:
MainWindow是主窗口。AddressWidget为窗口中的窗口中心的显示地址信息的一个子Widget。AddDialog为点击添加后弹出的添加地址信息弹框。NewAddressTab是第一个tab页,显示一行提示信息和一个添加按钮。TableModel是管理AddressWidget中数据的模式。
具体实现:
TableModel类:
TableModel继承自QAbstractTableModel,QAbstractTableModel类提供了一个抽象模型,通过继承它来创建一个表模型。
- QAbstractTableModel提供了一组标准的接口,它将数据表示为一个二维数组。QAbstractTableModel类不可以直接使用,必须通过子类化来使用。
- 当子类化QAbstractTableModel时,必须实现rowCount()、columnCount()和data()方法。最好还要实现headerData()方法。
- 要使TableModel是可编辑的,必须实现insertRows()、removeRows()、setData()、flags()。
- insertColumns()和removeColumns()方法来添加或者删除。
TableModel类中有一个成员变量listOfPairs,QList类型,存储姓名和地址信息对。
构造函数:
这里使用了两个构造函数,一个是默认构造函数,使用TableModel自己的QList<QPair<QString, QString>>。第二个构造函数为了方便起见,用参数值初始化模型中的QList<QPair<QString, QString>>。
rowCount和columnCount函数:
rowCount()和columnCount()函数返回TableModel的行数和列数。rowCount()的值将根据添加到地址簿的联系人数量而变化。columnCount()的值固定为2,因为我们只需要“姓名(name)”和“地址(address)”这两列。
Q_UNUSED(): 这个宏可以防止编译器生成关于未使用参数的警告。
data函数:
data()函数根据参数提供的模型索引值和数据角色返回数据的Name或Address。
QVariant: 表示最常见Qt数据类型的联合。即可以充当任何数据类型。
QModelIndex: QModelIndex类是专门用于定位数据模型中的数据的。QModelIndex包含指定项在模型中的位置所需的所有信息。可以使用row()、column()和parent()来创建一个项的QModelIndex,并用创建的QModelIndex作为参数获取项的信息。模型中的顶级项是没有父索引的,所以顶级项的父索引可以设为空。
Qt::DisplayRole: 模型中的数据元素都有自己的角色,即在视图中表示数据的显示类型。Qt::DisplayRole表示以文本形式呈现的数据(QString);Qt::DecorationRole表示以图标形式呈现的数据。(QColor, QIcon或QPixmap);Qt::EditRole表示在视图中以可编辑形式呈现的数据(QString)等。
headerData函数:
这个函数的作用是:显示表的标题。这里表的标题有两个,一个是“Name”,一个是“Address”。参数为指定方向、角色和节的数据。对于水平标题,节号表示列号;对于垂直标题,节号表示行号。我们只需要像例子中这样进行重写,就可以实现“Name”和“Address”这两个标题了。
insertRows函数:
insertRows()函数要在添加新数据(setData)之前被调用,否则数据将会显示不出来。调用beginInsertRows()和endInsertRows()函数,来确保所有有关联的视图都可以接收到这些变化。函数中将数据插入到本地的数据列表中。
beginInsertRows和endInsertRows: 开始行插入操作和结束插入操作。当在子类中重新实现insertRows()时,必须在将数据插入模型的底层数据存储之前调用beginInsertRows函数,在将数据插入模型的底层数据存储之后调用endInsertRows函数。
position: 数据将要插入的位置的行号。
rows: 将要出入的数据的行数。
removeRows函数:
removeRows()函数用来删除一行数据。这里的beginRemoveRows()和endRemoveRows()和上面一样。函数中将数据在本地数据列表中删除掉。
setData函数:
setData()函数将数据逐项插入表中,注意是逐项而不是逐行插入。这意味着要填充地址簿中的一行,必须调用setData()两次,因为每一行有两列。setData()函数中通过数据项的行和列进行定位,改变项的值,并将数据替换掉本地数据列表中的这一项。发出datachchanged()信号。
datachchanged: 这里的datachchanged()信号非常重要,它用来告诉所有有关联的视图更新它们的显示。
isValid: 如果模型索引有效,则返回true;否则返回false。
flags函数:
flags()函数的作用是用来返回给定索引的项目是否可编辑的标志。但是本例中没有使用QTableView对象的编辑特性。作者意图设置Qt::ItemIsEditable标志,想允许TableModel可以被编辑,但它们在这个项目里面没有用到。
getList函数:
AddressWidget类:
- AddressWidget类继承自QTabWidget(选项卡小部件)。选项卡小部件提供了一个选项卡栏(参见QTabBar)和一个“页面区域”,用于显示与每个选项卡相关的页面。默认情况下,标签栏显示在页面区域的上方,但也可以使用不同的配置(参见TabPosition)。每个选项卡都与一个不同的小部件(称为页面)相关联。页面区域只显示当前页面;所有其他页面都是隐藏的。用户可以通过点击其标签或按下Alt+字母快捷键来显示不同的页面。
- AddressWidget类是本例中的主要类,它提供了添加(addEntry)、编辑(editEntry)和删除(removeEntry)联系人、将联系人保存到文件(writeToFile)和从文件加载联系人(readFromFile)的功能。
- 该类中还包含一个信号selectionChanged,通知MainWindow选中的行发生了变化。
- setupTabs()是一个私有函数,主要用来实现标签的创建。
- 三个成员变量分别是TableModel实例、NewAddressTab实例和QSortFilterProxyModel实例。
构造函数:
AddressWidget构造中主要实现了实例化NewAddressTab和TableModel。并连接NewAddressTab的sendDetails信号到addEntry槽函数。添加NewAddressTab页,并使用setupTabs()设置9个TableMoodel页选项卡。
addEntry函数(无参):
这个函数同NewAddressTab中的addEntry函数,实例化一个AddDialog对象,弹出添加数据的弹框。然后调用带参的addEntry函数来真正的将联系人添加到表中。
addEntry函数(有参):
基本验证是在这个addEntry()函数中完成的,主要实现了防止地址簿中出现重复条目。
如果要插入的数据没有在列表中,那么在table的顶部中插入一行,并将"name"设置到第一个项中,"address"设置到第二个项中。注:编辑模式在这个示例中没有用到。
此时因为表中已经有数据了,所以调用removeTab将添加页删除。
如果有重复的,就会弹出一个提示框。
contains: 如果列表中包含值,则返回true;否则返回false。
indexOf: 返回小部件占用的页面的索引位置,如果找不到小部件,则返回-1。
QMessageBox: QMessageBox类提供了一个模态对话框,用于通知用户或向用户提示信息并接收回复。
editEntry函数:
编辑条目用来更新联系人地址。本示例中的姓名是不允许修改的。
首先,我们使用QTabWidget::currentWidget()获取活动标签的QTableView对象。然后,我们从tableView中提取selectionModel来获取所选索引。接下来,从用户想要编辑的行提取数据。将该数据显示在AddDialog的实例中。并且只有当对对话框中的数据进行了更改时,表才会更新。
removeEntry函数:
使用removeEntry()函数删除条目。
通过QItemSelectionModel对象selectionModel来访问所选行,从而删除所选行。当用户删除了地址簿中的所有联系人时,newAddressTab会重新添加到AddressWidget中。
setupTabs函数:
setupTabs()函数用于设置AddressWidget中的9个字母组标签、表视图和代理模型。每个代理模型依次被设置为根据使用不区分大小写的QRegExp对象的相关字母组过滤联系人名称。表视图也使用相应的代理模型的sort()函数按升序排序。
每个表视图的selectionMode设置为QAbstractItemView::SingleSelection, selectionBehavior设置为QAbstractItemView::SelectRows,允许用户同时选择一行中的所有项。每个QTableView对象都会自动给出一个QItemSelectionModel,用于跟踪所选索引。
readFromFile函数:
readFromFile()函数加载一个包含地址簿中所有联系人的文件,以前使用writeToFile()保存。QDataStream用于将.dat文件的内容读入一对列表中,并使用addEntry()添加其中的每一对。
writeToFile函数:
writeToFile()函数用于保存一个包含地址簿中所有联系人的文件。文件保存为自定义的“.dat”格式。qpair的QList的内容使用QDataStream写入文件。如果无法打开文件,会弹出QMessageBox提示错误信息。
NewAddressTab类:
NewAddressTab类是这里的第一个tab页,当地址簿中没有数据是,就会显示这个tab页提示用户添加数据,当地址簿中有数据时,这个tab页就不展示了。
这个类主要显示提示信息和一个添加按钮。包含一个添加按钮点击的槽函数,和一个向外界发送数据信息的信号。
构造函数:
构造函数主要实现窗口部件的创建、属性的设置以及信号的连接。
addEntry函数:
这个函数是“Add”按钮的槽函数,槽函数中完成了实例化一个AddDialog对象,即弹出添加数据的弹框。点击“ok”后通过发出sendDetails()信号,从对话框中提取数据并将其发送到AddressWidget的addEntry()槽函数中。
- 这里我们可以看到获取对话框的数据是在exec()函数执行完成之后获取的,这样做是可以的,因为对话框的生命周期在整个addEntry()中,所以只要没有走出addEntry()函数,整个对话框都是存在的。
AddDialog类:
AddDialog类实现的是添加数据弹框,它提供了一个QLineEdit用来输入姓名和一个QTextEdit用来输入地址信息来将数据输入到地址簿中。包里面含了两个Label,两个编辑框和两个按钮。只有一个成员函数是构造函数。
构造函数:
构造函数中完成了这个弹框所有的工作,包括创建所有的小部件,并对部件进行布局。连接按钮点击信号到槽函数。
accept: 隐藏模态对话框,并将结果DialogCode设置为Qt::Accepted(1)。
reject: 隐藏模态对话框,并将结果DialogCode设置为Qt::Rejected(0)。
所以我们在对话框exec()函数的返回值中可以判断是否要保存这条数据。
MainWindow类:
MainWindow类扩展了QMainWindow,并实现了操作地址簿所需的菜单和操作。主要包括各种小部件的实例,创建菜单方法,打开文件方法,保存文件方法,更新操作方法。
构造函数:
MainWindow的构造函数实例化AddressWidget,将其设置为其中心部件,并调用createMenus()函数。
createMenus函数:
createMenus()函数用来设置File和Tools菜单,并将它们触发的信号连接到对应的槽函数上。编辑和删除操作在默认情况下是禁用的,因为这样的操作不能在空的地址簿上执行。只有当添加了一个或多个联系人时才启用。将AddressWidget的selectionChanged()信号连接到updateActions()。
openFile函数:
该函数的作用是允许用户通过打开文件对话框选择一个文件。所选文件必须是包含地址簿联系人的自定义.dat文件。这个函数是一个连接到File菜单中的openAct的槽函数。
saveFile函数:
saveFile()函数允许用户通过保存文件对话框保存文件。这个函数是一个连接到File菜单中的saveAct的槽函数。
updateActions函数:
updateActions()函数启用和禁用编辑和删除条目取决于地址簿的内容。如果地址簿为空,则禁用这些操作;否则,它们是启用的。这个函数是一个连接到AddressWidget的selectionChanged()信号的槽函数。
一点思考:
- 在选择模型时,要充分考虑自己的需求选择合适的模型类。
- 多了解Qt自带的一些类的功能,避免在写程序时自己造轮子。
- 要提升自己写抽象类的能力。
getList()函数返回本地的listOfPairs对象,该对象保存地址簿中的列表的信息。
- 点赞
- 收藏
- 关注作者
评论(0)