file_selector_combobox.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. from PyQt5.QtWidgets import QGroupBox, QComboBox, QVBoxLayout, QFileDialog, QSizePolicy, QLabel
  2. from PyQt5.QtCore import QSettings, pyqtSlot, pyqtSignal, QCoreApplication
  3. import os
  4. import pathlib as pl
  5. from collections import OrderedDict
  6. from view.gui.application_settings import get_view_qsettings_manager
  7. class SingleFileSelectorHComboBox(QGroupBox):
  8. return_filename_signal = pyqtSignal(str, name="return filename")
  9. def new_selection_handler(self, label):
  10. filename, used_filter = QFileDialog.getOpenFileName(parent=self,
  11. caption=f"Select a {self.file_type} file",
  12. directory=self.get_default_directory(),
  13. filter=self.file_filter)
  14. possibly_index = self.combo_box.findText(filename)
  15. if filename:
  16. if possibly_index == -1:
  17. self.combo_box.addItem(filename)
  18. self.combo_box.setCurrentIndex(self.combo_box.count() - 1)
  19. else:
  20. self.combo_box.setCurrentIndex(possibly_index)
  21. self.return_filename(filename)
  22. def list_clearance_handler(self, label):
  23. entries_to_remove = []
  24. for index in range(self.combo_box.count()):
  25. entry = self.combo_box.itemText(index)
  26. if entry not in self.entry_handler_orderedDict.keys():
  27. entries_to_remove.append(entry)
  28. self.remove_entries(entries_to_remove)
  29. def __init__(self, parent, groupbox_title="File Selector",
  30. file_type="", file_filter="All Files(*.*)", comment=None):
  31. super().__init__(title=groupbox_title, parent=parent)
  32. self.file_type = file_type
  33. self.file_filter = file_filter
  34. vbox = QVBoxLayout(self)
  35. if comment is not None:
  36. vbox.addWidget(QLabel(comment, parent))
  37. self.combo_box = QComboBox(self)
  38. self.entry_handler_orderedDict = OrderedDict()
  39. self.entry_handler_orderedDict[f"--Please choose a {file_type} file--"] = None
  40. self.entry_handler_orderedDict[f"--Select a new {file_type} file--"] = self.new_selection_handler
  41. self.entry_handler_orderedDict[f"--Clear this list--"] = self.list_clearance_handler
  42. self.inaction_entries = [f"--Please choose a {file_type} file--"]
  43. self.combo_box.addItems(self.entry_handler_orderedDict.keys())
  44. self.combo_box.setCurrentIndex(0)
  45. self.combo_box.setDuplicatesEnabled(False)
  46. self.combo_box.activated.connect(self.combo_box_activated)
  47. vbox.addWidget(self.combo_box)
  48. self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
  49. def set_file_type(self, file_type):
  50. self.file_type = file_type
  51. def set_file_filter(self, file_filter):
  52. self.file_filter = file_filter
  53. def get_current_file_list(self):
  54. return [self.combo_box.itemText(ind) for ind in range(self.combo_box.count())
  55. if self.combo_box.itemText(ind) not in self.entry_handler_orderedDict.keys()]
  56. def get_default_directory(self):
  57. return os.path.expanduser("~")
  58. def set_entry(self, entry):
  59. possible_index = self.combo_box.findText(entry)
  60. if possible_index < 0:
  61. raise ValueError(f"Entry {entry} not found in combobox")
  62. else:
  63. self.combo_box.setCurrentIndex(possible_index)
  64. def get_current_entry(self):
  65. current_text = self.combo_box.currentText()
  66. if current_text in self.entry_handler_orderedDict.keys():
  67. return None
  68. else:
  69. return current_text
  70. @pyqtSlot(int, name="combo box activation handler")
  71. def combo_box_activated(self, index):
  72. label = self.combo_box.itemText(index)
  73. for entry, handler in self.entry_handler_orderedDict.items():
  74. if label == entry and handler is not None:
  75. handler(label)
  76. return
  77. if label not in self.inaction_entries:
  78. self.return_filename(self.combo_box.currentText())
  79. def remove_entries(self, entries):
  80. [self.combo_box.removeItem(self.combo_box.findText(x)) for x in entries]
  81. def return_filename(self, filename):
  82. self.return_filename_signal.emit(filename)
  83. class MultiFileSelectorHComboBox(SingleFileSelectorHComboBox):
  84. return_filenames_signal = pyqtSignal(list, name="return filename")
  85. def multi_selection_handler(self, label):
  86. filenames, used_filter = QFileDialog.getOpenFileNames(parent=self,
  87. caption=f"Select a {self.file_type} file",
  88. directory=self.get_default_directory(),
  89. filter=self.file_filter)
  90. if len(filenames):
  91. entry = ",".join([pl.Path(x).name for x in filenames])
  92. self.combo_box.addItem(entry)
  93. self.combo_box.setCurrentIndex(self.combo_box.count() - 1)
  94. self.inaction_entries.append(entry)
  95. self.return_filename(filenames)
  96. def __init__(self, parent, groupbox_title="File Selector",
  97. file_type="", file_filter="All Files(*.*)", comment=None):
  98. super().__init__(parent, groupbox_title, file_type, file_filter, comment)
  99. additional_entry = f"--Select multiple {self.file_type} files--"
  100. self.entry_handler_orderedDict[additional_entry] = self.multi_selection_handler
  101. self.combo_box.insertItem(2, additional_entry)
  102. def return_filename(self, filename):
  103. if type(filename) is str:
  104. return self.return_filenames_signal.emit([filename])
  105. elif type(filename) is list:
  106. return self.return_filenames_signal.emit(filename)
  107. else:
  108. raise(TypeError(f"Can only return str or list, got {type(filename)}"))
  109. def get_file_selector_combobox_using_settings(multiple_selection_allowed=False):
  110. if multiple_selection_allowed:
  111. super_class = MultiFileSelectorHComboBox
  112. else:
  113. super_class = SingleFileSelectorHComboBox
  114. class FileSelectorHComboBoxUsingSettingsList(super_class):
  115. def __init__(self, parent, groupbox_title, use_list_in_settings, settings_list_value_filter=lambda x: True,
  116. default_directory=None, file_type="", file_filter="All Files(*.*)",
  117. comment=None):
  118. super().__init__(parent, groupbox_title, file_type, file_filter, comment)
  119. self.settings_list = use_list_in_settings
  120. self.default_directory = default_directory
  121. if self.default_directory is not None:
  122. if pl.Path(default_directory).is_dir():
  123. self.default_directory = default_directory
  124. else:
  125. self.default_directory = None
  126. settings = get_view_qsettings_manager()
  127. # add files from internal settings, checking if they exist and satisfy <settings_list_value_filter>
  128. if settings.contains(self.settings_list):
  129. file_list = settings.value(self.settings_list, type=list)
  130. file_list_existing = [x for x in file_list if os.path.isfile(x)]
  131. settings.setValue(self.settings_list, file_list_existing)
  132. self.combo_box.addItems([x for x in file_list_existing if settings_list_value_filter(x)])
  133. # if the <self.settings_list> does not exist, initialize it to empty list
  134. else:
  135. settings.setValue(self.settings_list, [])
  136. def get_default_directory(self):
  137. current_file_list = self.get_current_file_list()
  138. if self.default_directory is not None:
  139. return self.default_directory
  140. elif len(current_file_list):
  141. return str(pl.Path(current_file_list[-1]).parent)
  142. else:
  143. return os.path.expanduser("~")
  144. def return_filename(self, filename):
  145. settings = get_view_qsettings_manager()
  146. current_file_list = settings.value(self.settings_list, type=list)
  147. if filename not in current_file_list:
  148. if type(filename) is str:
  149. filenames = [filename]
  150. elif type(filename) is list:
  151. filenames = filename
  152. else:
  153. raise NotImplementedError
  154. # update internal settings after deduplication
  155. settings.setValue(self.settings_list, list(set(current_file_list + filenames)))
  156. super().return_filename(filename)
  157. def remove_entries(self, entries):
  158. settings = get_view_qsettings_manager()
  159. current_file_list = settings.value(self.settings_list, type=list)
  160. for entry in entries:
  161. current_file_list.remove(entry)
  162. self.combo_box.removeItem(self.combo_box.findText(entry))
  163. settings.setValue(self.settings_list, current_file_list)
  164. return FileSelectorHComboBoxUsingSettingsList