【Python随笔】PyQt5的QListView兼容左键双击事件和右键上下文菜单的方法

近期笔者因工作原因,需要做一个安卓手机的文件浏览功能,集成在笔者以前用PyQt5做的一个的工具当中。文件浏览功能大概做成这样:

  • 一个列表界面,列出某个目录下的所有文件名(不区分文件和文件夹)
  • 双击某个文件名,尝试进入这个文件名代表的文件夹(文件的情况会失败)
  • 右键某个文件名,弹出上下文菜单,可以进入这个文件名对应的文件夹,也可以复制路径到其他的输入框

其中,文件列表选型用了QListView组件,但在实现兼容双击进入文件夹+右键菜单功能时,稍微踩了下坑。为了解决这个问题,笔者在网上查阅了许多资料,最后找到一种解决方法,决定记录于本文当中。

首先需要了解,Qt对于QListView这类数据容器组件,是遵循MVC的设计模式的。QListView数据的初始化,方法是这样的:

1
2
3
4
5
data = ['1', '2', '3']
model = QStringListModel()
model.setStringList(data)
listview = QListView()
listview.setModel(model)

可以看到,是data -> model -> view这样的关系呈现。

要让listview同时支持双击和右键事件,常见的想法是用doubleClickedclicked之类的signalconnect对应的事件委托。但这里有一个误区:clickedconnect事件委托这种情况下,不论在listview里单击鼠标左键或者单击右键,都能触发鼠标单击事件,而如果同时connectdoubleClicked的委托,doubleClicked的委托会无法执行。这样的话,就无法同时满足左键双击和右键上下文菜单功能。

看似这个问题无法解决,实际上Qt还是给开发者留下了一个后门——QWidget本身有一个setContextMenuPolicy接口去设置上下文菜单策略,而这个上下文菜单就是右键触发的。因此,我们可以通过绑定doubleClicked事件委托,同时setContextMenuPolicy为自定义菜单模式CustomContextMenu,并绑定customContextMenuRequested这个signal到一个自动弹出上下文菜单的方法,就可以事件左键双击事件和右键上下文菜单的兼容了。

代码样例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def on_double_clicked(idx):
# idx为QModelIndex类型,通过row方法获取数据索引值
print(f'triggered double-click -> {data[idx.row()]}')


def on_trigger_menu_action():
# 通过selectedIndexes方法可以获得点中的所有项
selected_indexes = listview.selectedIndexes()
if len(selected_indexes) > 0:
data_idx = selected_indexes[0].row()
print(f'triggered context menu -> {data[data_idx]}')


def on_custom_context_menu_requested(pos):
ctx_menu = QtGui.QMenu()
menu_action = popMenu.addAction('打印信息')
menu_action.triggered.connect(on_trigger_menu_action)
ctx_menu.exec_(QtGui.QCursor.pos()) # 由当前鼠标位置弹出菜单


listview.doubleClicked.connect(on_double_clicked)
listview.setContextMenuPolicy(Qt.CustomContextMenu)
listview.customContextMenuRequested.connect(on_custom_context_menu_requested)

doubleClicked的场景下,可以通过传参的QModelIndex实例直接获取数据的索引值,从而拿到数据;在customContextMenuRequested的场景下,可以通过listviewselectedIndexes获取所有选中项的QModelIndex,进而也是通过row接口,就可以知道哪个下标的数据被选中了。

版权声明
本文为博客HiKariのTechLab原创文章,转载请标明出处,谢谢~~~