#python #pyqt5 #python-3.8
#python #pyqt5 #python-3.8
Вопрос:
Я создал элемент 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('amp;File', self)
self.recent = file_menu.addMenu('amp;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 на диске (пример кода не включает этот бит). Когда пользователь открывает новый файл, имя файла успешно вставляется в список последних файлов. При следующем запуске приложения в меню «Последние файлы» отображается обновленный список.
Однако я бы хотел, чтобы список последних файлов в меню был динамическим. Как мне лучше всего выполнить вызов для восстановления меню «Последние файлы» в режиме реального времени?
РЕДАКТИРОВАТЬ — я попытался прояснить вопрос, чтобы сосредоточиться на динамическом обновлении пункта меню, а не на постоянном обслуживании списка файлов.
Ответ №1:
Если вы хотите постоянно сохранять информацию, вы должны сохранить эту информацию на жестком диске (в файле), и в 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("amp;File", self)
self.recentfiles_menu = file_menu.addMenu("amp;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_())
Комментарии:
1. Спасибо, что нашли время ответить на вопрос. Похоже, вы рекомендуете использовать QSettings вместо моего текущего подхода, который сохраняет настройки приложения в файле json. Другими словами, я уже могу сохранить список недавно открытых файлов в моем json и вызвать их, когда список меню будет заполнен изначально. Необходимо ли использовать QSettings для динамического обновления строки меню (т. Е. Единственным способом)? Причина, по которой я спрашиваю, заключается в том, что ваша рекомендация потребует рефакторинга других аспектов моего приложения (несколько других аспектов управляются централизованно с помощью подхода json).
2. @DaveL17 1) Вы никогда не упоминали, что используете json, 2) Мое решение — это просто рекомендация, та же логика также применяется, если вы используете json, yaml и т.д. 3) Моя логика предназначена для любого типа класса, который позволяет вам постоянно сохранять информацию, 4) Я не указываю на то, что один лучше другого, поскольку это будет зависеть от вашего приложения, но в вашем случае, будучи простым приложением, ни один из них не будет представлять интереса.
3. @DaveL17 5) Пожалуйста, не думайте, что мы волшебники, которые угадывают, что есть в вашем проекте, мы фокусируемся только на коде, который вы предоставляете, не более того. Последнее, на что я указываю, потому что действительно раздражает, что после предоставления вам ответа вы сообщаете о других условиях.
4. @DaveL17 Наконец, если вы хотите использовать json, вам просто нужно изменить методы load_settings и save_settings
5. Извиняюсь. Я мог бы более четко сформулировать свой вопрос. Инструкция под блоком кода предназначена для передачи моего текущего использования json — «Это работает хорошо. Когда пользователь открывает новый файл, имя файла вставляется в список последних файлов (а также записывается в файл настроек json). При следующем запуске приложения в меню «Последние файлы» отображается обновленный список. » Я явно должен был сформулировать это лучше. Извините, что вызвал у вас разочарование. Я действительно не хочу показаться неблагодарным и очень ценю, что вы нашли время.
Ответ №2:
Когда содержимое меню является динамическим, я обычно предпочитаю использовать aboutToShow
сигнал QMenu, который запускается непосредственно перед его открытием, и подключается к функции, которая очищает его и обновляет его содержимое.
Я также использую QAction.setData
для хранения имени файла, что позволяет отображать настраиваемый текст (например, если путь слишком длинный, мы можем исключить его или показать только имя файла вместо полного пути).
class Application(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super(Application, self).__init__(*args, **kwargs)
file_menu = self.menuBar().addMenu('amp;File')
self.recent_menu = file_menu.addMenu('amp;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('amp;{}. {}'.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)
Комментарии:
1. Это отличный совет, спасибо. Я смог применить ответ @eyllanesc с хорошим эффектом, но мне также нравится этот подход. Оба ваших ответа напоминают мне, что мне нужно быть бдительным, чтобы сделать мои методы более Pythonic. Приветствия.