PyQt5系列教程(36): QQ模拟QListView 2


上期我们介绍了QAbstractListModel子类化之后,我们模型(Model)的实现,本期我们介绍一下QListView的使用,这里的使用也是自定义一个类,当然是继承了QListView。
核心代码
class ListView(QListView):
map_listview = []
def __init__(self):
super().__init__()
self.m_pModel = ListModel()
self.setModel(self.m_pModel)
def contextMenuEvent(self, event):
hitIndex = self.indexAt(event.pos()).column()
if hitIndex > -1:
pmenu = QMenu(self)
pDeleteAct = QAction("删除",pmenu)
pmenu.addAction(pDeleteAct)
pDeleteAct.triggered.connect(self.deleteItemSlot)
pSubMenu = QMenu("转移联系人至" ,pmenu)
pmenu.addMenu(pSubMenu)
for item_dic in self.map_listview:
pMoveAct = QAction(item_dic['groupname'] ,pmenu)
pSubMenu.addAction(pMoveAct)
pMoveAct.triggered.connect(self.move)
pmenu.popup(self.mapToGlobal(event.pos()))
def deleteItemSlot(self):
index = self.currentIndex().row()
if index > -1:
self.m_pModel.deleteItem(index)
def setListMap(self, listview):
self.map_listview.append(listview)
def addItem(self, pitem):
self.m_pModel.addItem(pitem)
def move(self):
tolistview = self.find(self.sender().text())
if tolistview is self:
prelistview = self.sender().text()
QMessageBox.warning(self, "警告", "该联系人就在{},还怎么移动啊!".format(prelistview))
else:
index = self.currentIndex().row()
pItem = self.m_pModel.getItem(index)
tolistview.addItem(pItem)
self.m_pModel.deleteItem(index)
def find(self, pmenuname):
for item_dic in self.map_listview:
if item_dic['groupname'] == pmenuname:
return item_dic['listview']
这段代码分为两大部分:自定义右键菜单(上下文菜单)和增删移的操作。
map_listview = []
map_listview是一个类变量,那么这个类变量与实例变量有什么区别呢?
类变量为所有实例共享的,而实例变量则是每个实例独有的。举个简单的例子:
class P:
list = [0]
def __init__(self):
self.a = 1
def out(self):
print(self.list)
print('a = ' + str(self.a))
pp = P()
ppp = P()
pp.out()
ppp.out()
print('*'*10)
pp.list.append(1)
pp.a = 2
pp.out()
ppp.out()
print(ppp.list)
#执行结果
[0]
a = 1
[0]
a = 1
**********
[0, 1]
a = 2
[0, 1]
a = 1
[0, 1]
从这个例子当中我们可以看出,当pp这个实例给list增加一个数据之后,ppp.list也随之改变了。
为什么写这个呢?
因为我们要利用map_listview保存QListView对象和分组名称的对应关系。因为这样我们就能靠分组名称找到对应的QListView对象,根据QListView对象就行调用对应的QAbstractListModel的子类了。
so,我们每新建一个分组就会自动初始化随机的联系人信息了。
self.m_pModel = ListModel()
self.setModel(self.m_pModel)
设置要呈现的视图的模型,这里我们用的是自定义的ListModel类。
def contextMenuEvent(self, event):
hitIndex = self.indexAt(event.pos()).column()
if hitIndex > -1:
pmenu = QMenu(self)
pDeleteAct = QAction("删除",pmenu)
pmenu.addAction(pDeleteAct)
pDeleteAct.triggered.connect(self.deleteItemSlot)
pSubMenu = QMenu("转移联系人至" ,pmenu)
pmenu.addMenu(pSubMenu)
for item_dic in self.map_listview:
pMoveAct = QAction(item_dic['groupname'] ,pmenu)
pSubMenu.addAction(pMoveAct)
pMoveAct.triggered.connect(self.move)
pmenu.popup(self.mapToGlobal(event.pos()))
这个函数主要是实现上下文菜单的,也就是传说中的单击右键菜单。如下图:
hitIndex = self.indexAt(event.pos()).column()
这个函数是说返回鼠标指针相对于接收事件的小部件的位置,然后我们根据这个位置的坐标返回视口坐标点处的项目的模型索引,最后根据索引返回此模型索引引用的列(感觉说得好绕口啊~!)。
if hitIndex > -1:
pmenu = QMenu(self)
pDeleteAct = QAction("删除",pmenu)
pmenu.addAction(pDeleteAct)
pDeleteAct.triggered.connect(self.deleteItemSlot)
pSubMenu = QMenu("转移联系人至" ,pmenu)
pmenu.addMenu(pSubMenu)
这段比较好理解,就是新增上下文菜单,这里不做过多介绍,因为前面的文章讲解过了,可以参考:
当我们点击删除菜单的时候调用deleteItemSlot()函数。
for item_dic in self.map_listview:
pMoveAct = QAction(item_dic['groupname'] ,pmenu)
pSubMenu.addAction(pMoveAct)
pMoveAct.triggered.connect(self.move)
还记得上面介绍map_listview的作用吗?
这里我们将每个分组名称取出,新建一个QAction对象,加入到pSubMenu当中。点击这个每个分组的时候就会执行联系人转移分组操作,这里就是move()的调用。
pmenu.popup(self.mapToGlobal(event.pos()))
显示菜单,以便动作QAction对象在指定的全局位置p处。这里的全局位置p是根据小部件的本地坐标转换为全局坐标的,故使用QWidget.mapToGlobal()。
def deleteItemSlot(self):
index = self.currentIndex().row()
if index > -1:
self.m_pModel.deleteItem(index)
返回当前项目的模型索引,并根据索引返回此模型索引引用的行,然后到模型里面删除相应的数据。
def setListMap(self, listview):
self.map_listview.append(listview)
将分组名称和QListView对象这个字典增加到map_listview数据列表中。我们会在QQ这个类里面用到。
def addItem(self, pitem):
self.m_pModel.addItem(pitem)
新增一个联系人,我们在转移联系人的时候会用到。
def move(self):
tolistview = self.find(self.sender().text())
if tolistview is self:
prelistview = self.sender().text()
QMessageBox.warning(self, "警告", "该联系人就在{},还怎么移动啊!".format(prelistview))
else:
index = self.currentIndex().row()
pItem = self.m_pModel.getItem(index)
tolistview.addItem(pItem)
self.m_pModel.deleteItem(index)
def find(self, pmenuname):
for item_dic in self.map_listview:
if item_dic['groupname'] == pmenuname:
return item_dic['listview']
这个函数是实现联系人转移的。
tolistview = self.find(self.sender().text())
我们根据点击的分组名称找到对应的QListView对象。find函数是自定义的。
if tolistview is self: