动态更新PYQT菜单项



我使用以下构造构造了一个Qmenu项,其中包含最近打开的文件列表。我想实时更新菜单栏,这样我就不必等到下次应用程序运行时才更新菜单栏。

import sys
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtGui import QPixmap

class Application(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super(Application, self).__init__(*args, **kwargs)
self.settings = {}
self.settings['recent files'] = ['filename1', 'filename2', 'filename3']
QtWidgets.QMainWindow.__init__(self)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.menu_bar()
def menu_bar(self):
file_menu = QtWidgets.QMenu('&File', self)
self.recent = file_menu.addMenu('&Open Recent')
for filename in self.settings['recent files']:
self.recent.addAction(f'{filename}', lambda source=filename: self.dialog_open_file(file_name=source))
self.menuBar().addMenu(file_menu)
def dialog_open_file(self):

self.settings['recent files'] = ['filename 4', 'filename1', 'filename2', 'filename3']
self.update_recent_files()
# do lots more stuff
def update_recent_files(self):
# Update list of recent files in Open Recent menubar  <---- 
qApp = QtWidgets.QApplication(sys.argv)
application_window = Application()
application_window.setWindowTitle(f"My App")
application_window.show()
sys.exit(qApp.exec_())

我的实际代码工作得很好(除了实时更新菜单)。它将最近的文件列表写入磁盘上的持久json(示例代码不包含该位)。当用户打开一个新文件时,文件名将成功插入到最近的文件列表中。下次程序运行时,Recent Files菜单会显示更新的列表。

但是,我希望最近文件的菜单列表是动态的。我怎样才能最好地调用实时重建"最近文件"菜单?

编辑——我试图澄清这个问题,把重点放在菜单项的动态更新上,而不是放在持久的维护上文件列表

如果你想持久地保存信息,那么你必须将该信息保存在硬盘驱动器上(在文件中),并且在python和Qt中有许多替代方法,例如QSettings。逻辑是在创建窗口时加载信息,并在必要时保存它(例如当窗口关闭时)。

import sys
from functools import cached_property
from PyQt5 import QtCore, QtWidgets, QtGui

class Application(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(Application, self).__init__(parent)
QtWidgets.QMainWindow.__init__(self)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.create_menu_file()
self.load_settings()
button = QtWidgets.QPushButton("Load new files")
button.clicked.connect(self.another_task)
self.setCentralWidget(button)
def create_menu_file(self):
file_menu = QtWidgets.QMenu("&File", self)
self.recentfiles_menu = file_menu.addMenu("&Open Recent")
self.recentfiles_menu.triggered.connect(self.handle_triggered_recentfile)
self.menuBar().addMenu(file_menu)
@cached_property
def settings(self):
return QtCore.QSettings()
def load_settings(self):
filenames = self.settings.value("recent_files", [])
for filename in filenames:
self.add_recent_filename(filename)
def save_settings(self):
recentfiles = []
for action in self.recentfiles_menu.actions()[::-1]:
recentfiles.append(action.text())
self.settings.setValue("recent_files", recentfiles)
@QtCore.pyqtSlot(QtWidgets.QAction)
def handle_triggered_recentfile(self, action):
self.process_filename(action.text())
def add_recent_filename(self, filename):
action = QtWidgets.QAction(filename, self)
actions = self.recentfiles_menu.actions()
before_action = actions[0] if actions else None
self.recentfiles_menu.insertAction(before_action, action)
def process_filename(self, filename):
print(filename)
def closeEvent(self, event):
super(Application, self).closeEvent(event)
self.save_settings()
def another_task(self):
# DEMO
# load new filenames
counter = len(self.recentfiles_menu.actions())
filenames = [f"foo {counter}"]
for filename in filenames:
self.add_recent_filename(filename)

qApp = QtWidgets.QApplication(sys.argv)
application_window = Application()
application_window.setWindowTitle(f"My App")
application_window.show()
sys.exit(qApp.exec_())

当菜单的内容是动态的时,我通常更喜欢使用QMenu的aboutToShow信号,它在打开之前被触发,并连接到一个清除它并更新其内容的函数。

我还使用QAction.setData来存储文件名,这允许显示自定义文本(例如,如果路径太长,我们可以省略它,或者只显示文件名而不显示完整路径)。

class Application(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super(Application, self).__init__(*args, **kwargs)
file_menu = self.menuBar().addMenu('&File')
self.recent_menu = file_menu.addMenu('&Open recent')
self.recent_menu.aboutToShow.connect(self.update_recent_menu)
self.recent_menu.triggered.connect(self.open_file_from_recent)
self.settings = {}
def update_recent_menu(self):
self.recent_menu.clear()
for row, filename in enumerate(self.get_recent_files(), 1):
recent_action = self.recent_menu.addAction('&{}. {}'.format(
row, filename))
recent_action.setData(filename)
def get_recent_files(self):
recent = self.settings.get('recent files')
if not recent:
# just for testing purposes
recent = self.settings['recent files'] = ['filename 4', 'filename1', 'filename2', 'filename3']
return recent
def open_file_from_recent(self, action):
self.open_file(action.data())
def open_file(self, filename):
recent = self.get_recent_files()
if filename in recent:
recent.remove(filename)
recent.insert(0, filename)

print(filename)

最新更新