@frizzo
Hi, and welcome to the forum. I’ve worked with keeping a persistent recent files list in the past and I think you are on the right track. Without a more detailed code sample, I can not be sure what exactly is wrong with what you’ve been working on, but I have a basic working solution that you should be able to implement.
In the past, I’ve used a YAML file to store the data that I wanted to persist because it allows me to store lots of data in a human readable format. A JSON file would also be quite appropriate. However, since you mentioned that you are using pickle, I’ve used that in my examples. I am using PyQt6 instead of PySide6, but it’s my understanding that all the concepts will transfer directly.
I believe the main thing to keep in mind when wanting to keep your data in the same order when it is saved is to ensure you’re using a container that doesn’t automatically sort it’s entries for you. A list and an OrderedDict are both appropriate and which one you use is solely dependent on how you want to interact with the data. You don’t want to use a standard dict or a set as both of these will alphabetize the entries.
I’ve created two different sample classes which extend QComboBox to do what I believe you are looking for. I also took the liberty of using the ability to add both display text and associated data for each entry to keep the drop-down items more readable.
QComboBox using a list for data storage
import os
import pickle
from PyQt6.QtWidgets import QComboBox
class ComboBoxList(QComboBox):
def __init__(self, *args, **kwargs):
QComboBox.__init__(self, *args, **kwargs)
self.setAcceptDrops(True)
self.setPlaceholderText("Drag files here...")
self.pickle_file = "combo_list.pkl"
# Load data from pickle file and assign it to the combobox
try:
with open(self.pickle_file, "rb") as file:
list_data = pickle.load(file)
print(f"Data type: {type(list_data)}")
for text, data in list_data:
self.addItem(text, data)
except (FileNotFoundError, pickle.UnpicklingError):
print("Pickle data not available")
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.acceptProposedAction()
else:
event.ignore()
def dropEvent(self, event):
if event.mimeData().hasUrls():
for url in event.mimeData().urls():
file_path = url.toLocalFile()
if file_path: # Ensure a valid local file path is obtained
file_name = os.path.basename(file_path)
self.insertItem(0, file_name, file_path)
self.setCurrentIndex(0)
self._save_list()
event.acceptProposedAction()
else:
event.ignore()
def _save_list(self):
cur_list = [
(self.itemText(i), self.itemData(i)) for i in range(self.count())
]
with open(self.pickle_file, "wb") as file:
pickle.dump(cur_list, file)
QComboBox using an OrderedDict for data storage
import os
import pickle
from collections import OrderedDict
from PyQt6.QtWidgets import QComboBox
class ComboBoxOrderedDict(QComboBox):
def __init__(self, *args, **kwargs):
QComboBox.__init__(self, *args, **kwargs)
self.setAcceptDrops(True)
self.setPlaceholderText("Drag files here...")
self.pickle_file = "combo_ordered_dict.pkl"
try:
with open(self.pickle_file, "rb") as file:
ordered_dict_data = pickle.load(file)
print(f"Data type: {type(ordered_dict_data)}")
for text, data in ordered_dict_data.items():
self.addItem(text, data)
except (FileNotFoundError, pickle.UnpicklingError):
print("Pickle data not available")
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.acceptProposedAction()
else:
event.ignore()
def dropEvent(self, event):
if event.mimeData().hasUrls():
for url in event.mimeData().urls():
file_path = url.toLocalFile()
if file_path: # Ensure a valid local file path is obtained
file_name = os.path.basename(file_path)
self.insertItem(0, file_name, file_path)
self.setCurrentIndex(0)
self._save_list()
event.acceptProposedAction()
else:
event.ignore()
def _save_list(self):
cur_ordered_dict = OrderedDict()
for i in range(self.count()):
cur_ordered_dict[f"{self.itemText(i)}"] = self.itemData(i)
with open(self.pickle_file, "wb") as file:
pickle.dump(cur_ordered_dict, file)
QMainWindow class to sample their functionality
import sys
from PyQt6.QtWidgets import (
QApplication,
QLabel,
QVBoxLayout,
QMainWindow,
QWidget,
)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Drag and Drop Files to ComboBox")
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QVBoxLayout(central_widget)
layout.addWidget(
QLabel(
"Persistent QComboBox list using a list storage containter:"
)
)
self.combo_box_list = ComboBoxList()
self.combo_box_list.setObjectName("QComboBox with list")
self.combo_box_list.currentIndexChanged.connect(
self.on_list_index_change
)
layout.addWidget(self.combo_box_list)
layout.addWidget(
QLabel(
"Persistent QComboBox list using an OrderedDict storage "
"conatainer:"
)
)
self.combo_box_ordered_dict = ComboBoxOrderedDict()
self.combo_box_ordered_dict.setObjectName("QComboBox with OrderedDict")
self.combo_box_ordered_dict.currentIndexChanged.connect(
self.on_ordered_dict_index_change
)
layout.addWidget(self.combo_box_ordered_dict)
def on_list_index_change(self):
self._print_combo_box_info(self.combo_box_list)
def on_ordered_dict_index_change(self):
self._print_combo_box_info(self.combo_box_ordered_dict)
def _print_combo_box_info(self, combobox):
print(f"{combobox.objectName()}; Type: {type(combobox)}; Text:"
f"{combobox.currentText()}; Data: {combobox.currentData()}")
if __name__ == "__main__":
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
sys.exit(app.exec())
Of course you will want to adjust things like file paths and overall functionality of the QCombBox to suit your needs, but both of the combo box classes will load the data that was saved to the pickle files so they start with the list they had when they closed.