You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			195 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			195 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Python
		
	
| import logging
 | |
| import os.path
 | |
| from typing import List, Tuple
 | |
| 
 | |
| from PyQt5 import QtWidgets
 | |
| from PyQt5.QtCore import Qt
 | |
| from PyQt5.QtGui import QStandardItem, QStandardItemModel
 | |
| 
 | |
| from ArtNet.gui.dialogs.collection_modify_dialog.collection_modify_dialog import Ui_Dialog
 | |
| 
 | |
| from ArtNet.gui.dialogs.image_select_dialog.image_sel_dialog import ImageSelectDialog
 | |
| from ArtNet.db.db_adapter import DBAdapter
 | |
| 
 | |
| 
 | |
| class CollectionModDialog(QtWidgets.QDialog):
 | |
|     """
 | |
|     Dialog to edit and modify a collection and its entries' ranking
 | |
|     """
 | |
| 
 | |
|     # TODO add way to increase and lower ranking for an entry (up and down buttons?)
 | |
|     # TODO add way to delete entry (button)
 | |
|     # TODO can I catch which item in a list is selected?
 | |
| 
 | |
|     def __init__(self, parent, db_connection: DBAdapter, edit_collection: bool = False):
 | |
|         super().__init__(parent)
 | |
|         self.parent = parent
 | |
|         self.__db = db_connection
 | |
| 
 | |
|         self.__curr_entries: List[Tuple[str, dict]] = list()  # [(entry_str, Dict[title, path, ranking])]
 | |
|         self.data = dict()
 | |
| 
 | |
|         self.setWindowTitle("Creating Collection")
 | |
| 
 | |
|         self.ui = Ui_Dialog()
 | |
|         self.ui.setupUi(self)
 | |
| 
 | |
|         if not edit_collection:
 | |
|             self.ui.collection_name_line.setReadOnly(True)
 | |
|             self.ui.description_browser.setReadOnly(True)
 | |
| 
 | |
|         self.ui.add_image_button.clicked.connect(self.on_add_art_clicked)
 | |
|         self.ui.optimize_ranking_button.clicked.connect(self.on_optimize_ranking_clicked)
 | |
| 
 | |
|     @staticmethod
 | |
|     def create_entry_string(art_data: dict) -> str:
 | |
|         """
 | |
|         Creates a string representation of the given art in the collection including its ranking string
 | |
|         """
 | |
|         title: str = art_data["title"] if art_data["title"] is not None else os.path.basename(art_data["path"])
 | |
|         if len(title) > 20:
 | |
|             title = title[-20:]
 | |
|         elif len(title) < 20:
 | |
|             while len(title) < 20:
 | |
|                 title += " "
 | |
|         entry_string = title + " | " + art_data["ranking"]
 | |
|         return entry_string
 | |
| 
 | |
|     def append_collection_entry(self, art_data: dict):
 | |
|         """
 | |
|         Append a new art entry into the collection
 | |
|         """
 | |
|         if len(self.__curr_entries) > 0:  # need to create even lower entry
 | |
|             lowest_r = None
 | |
|             for s, data in self.__curr_entries:
 | |
|                 r = data["ranking"]
 | |
|                 if lowest_r is None or lowest_r < r:
 | |
|                     lowest_r = r
 | |
| 
 | |
|             if lowest_r[-1] < "z":  # lower and not 'z'
 | |
|                 ranking = lowest_r
 | |
|                 ranking = ranking[:-1]
 | |
|                 ranking += chr(ord(lowest_r[-1]) + (ord('z')-ord(lowest_r[-1]))//2 + 1)  # last character + 1
 | |
|             else:
 | |
|                 ranking = lowest_r if lowest_r is not None else ""
 | |
|                 ranking += "m"
 | |
| 
 | |
|         else:  # first to add
 | |
|             ranking = "m"  # the middle
 | |
| 
 | |
|         art_data["ranking"] = ranking
 | |
|         title: str = art_data["title"] if art_data["title"] is not None else os.path.basename(art_data["path"])
 | |
|         if len(title) > 20:
 | |
|             title = title[-20:]
 | |
|         elif len(title) < 20:
 | |
|             while len(title) < 20:
 | |
|                 title += " "
 | |
| 
 | |
|         entry_string = title + " | " + art_data["ranking"]
 | |
|         self.__curr_entries.append((entry_string, art_data))
 | |
|         self.__curr_entries.sort(key=lambda x: x[1]["ranking"])
 | |
|         logging.debug(f"New collection list is: {self.__curr_entries}")
 | |
|         self.set_collection_entry_list(self.__curr_entries)
 | |
| 
 | |
|     def insert_collection_entry(self, art_data: dict, index: int):
 | |
|         """
 | |
|         Insert the art entry into the collection at position index.
 | |
|         This might shift other entries in their index but tries to minimize ranking string changes.
 | |
|         """
 | |
|         assert(index >= 0)
 | |
|         if len(self.__curr_entries)-1 > index:  # inserts between entries or in front of entries
 | |
|             conflicting_art = self.__curr_entries[index][1]
 | |
|             following_art = self.__curr_entries[index+1][1]
 | |
| 
 | |
|             if len(conflicting_art["ranking"]) == len(following_art["ranking"]):  # can I squeeze it in?
 | |
|                 if following_art["ranking"][-1] < conflicting_art["ranking"][-1] < "z":
 | |
|                     # both are not same and not "z" -> there might be space
 | |
|                     char_diff = ord(conflicting_art["ranking"][-1]) - ord(following_art["ranking"][-1])
 | |
|                     if char_diff > 1:  # can squeeze a letter between them
 | |
|                         ranking = conflicting_art["ranking"][:-1]
 | |
|                         ranking += chr(ord(conflicting_art["ranking"][-1]) + char_diff//2)
 | |
|                     else:
 | |
|                         ranking = conflicting_art["ranking"] + "m"
 | |
|                 else:
 | |
|                     ranking = conflicting_art["ranking"] + "m"
 | |
| 
 | |
|             elif conflicting_art["ranking"][-1] < "z":  # just insert it the plain way
 | |
|                 ranking = conflicting_art["ranking"]
 | |
|                 ranking = chr(ord(ranking[-1])+1)
 | |
|             else:
 | |
|                 ranking = conflicting_art["ranking"] + "m"
 | |
| 
 | |
|             art_data["ranking"] = ranking
 | |
|             entry_string = CollectionModDialog.create_entry_string(art_data)
 | |
|             self.__curr_entries.append((entry_string, art_data))
 | |
|             self.__curr_entries.sort(key=lambda x: x[1]["ranking"])
 | |
|             logging.debug(f"New collection list is: {self.__curr_entries}")
 | |
|             self.set_collection_entry_list(self.__curr_entries)
 | |
| 
 | |
|         else:  # inserts at the end
 | |
|             self.append_collection_entry(art_data)
 | |
| 
 | |
|     def collect_collection_details(self):
 | |
|         """
 | |
|         Collect the entered details about the collection and save them as self.data
 | |
|         """
 | |
|         self.data["name"] = self.ui.collection_name_line.text()
 | |
|         self.data["description"] = self.ui.description_browser.toPlainText().strip()
 | |
|         self.data["entries"] = self.__curr_entries
 | |
| 
 | |
|     def set_collection_entry_list(self, entries: list):
 | |
|         """
 | |
|         Set the entries in the list
 | |
|         :param entries:
 | |
|         :return:
 | |
|         """
 | |
|         item_model = QStandardItemModel(self.ui.entry_list)
 | |
| 
 | |
|         entries.sort(key=lambda x: x[1]["ranking"])
 | |
| 
 | |
|         for entry, _ in entries:
 | |
|             item = QStandardItem(entry)
 | |
|             flags = Qt.ItemIsEnabled
 | |
|             item.setFlags(flags)
 | |
| 
 | |
|             item.setData(Qt.Unchecked, Qt.CheckStateRole)
 | |
|             item.setCheckState(Qt.Checked)  # can just be checked, otherwise not in the entry list
 | |
| 
 | |
|             item_model.appendRow(item)
 | |
| 
 | |
|         item_model.itemChanged.connect(self.on_collection_entry_changed)
 | |
|         self.ui.entry_list.setModel(item_model)
 | |
| 
 | |
|     def exec_(self, collection_details: dict = None) -> dict:
 | |
|         if collection_details is not None:
 | |
|             self.ui.collection_name_line.setText(collection_details["name"])
 | |
|             self.ui.description_browser.setText(collection_details["description"])
 | |
|             self.__curr_entries = collection_details["entries"]
 | |
|             self.set_collection_entry_list([CollectionModDialog.create_entry_string(entry) for entry in
 | |
|                                             collection_details["entries"]])
 | |
| 
 | |
|         if super(CollectionModDialog, self).exec_() == QtWidgets.QDialog.Rejected:
 | |
|             return None
 | |
|         self.collect_collection_details()
 | |
| 
 | |
|         return self.data
 | |
| 
 | |
|     # callback method
 | |
|     def on_add_art_clicked(self):
 | |
|         logging.info("Clicked add art for collection")
 | |
|         dialog = ImageSelectDialog(db_connection=self.__db, parent=self)
 | |
|         art_data = dialog.exec_()
 | |
|         logging.info(f"Received selected art: {art_data}")
 | |
|         if art_data is None:  # nothing was selected
 | |
|             return
 | |
|         self.append_collection_entry(art_data)
 | |
| 
 | |
|     def on_optimize_ranking_clicked(self):
 | |
|         logging.info("Clicked optimize ranking for collection")
 | |
|         # TODO use some optimization algo to change the ranking strings to be minimal in length, with max distance
 | |
| 
 | |
|     def on_collection_entry_changed(self, item: QStandardItem):
 | |
|         logging.info(f"Removing entry {item.text()}")
 | |
|         entry_id = item.text().split(":")[0]
 | |
|         self.__curr_entries.remove(entry_id)
 |