From ea904da58bbacc7fd4c96dcafc68c51047345a29 Mon Sep 17 00:00:00 2001 From: Creepler13 Date: Mon, 29 Apr 2024 21:28:32 +0200 Subject: [PATCH 1/2] Folders to tags Cleanup + bugfix --- tagstudio/src/qt/modals/folders_to_tags.py | 274 +++++++++++---------- 1 file changed, 138 insertions(+), 136 deletions(-) diff --git a/tagstudio/src/qt/modals/folders_to_tags.py b/tagstudio/src/qt/modals/folders_to_tags.py index bb05083b..26b00e43 100644 --- a/tagstudio/src/qt/modals/folders_to_tags.py +++ b/tagstudio/src/qt/modals/folders_to_tags.py @@ -26,6 +26,58 @@ INFO = f'[INFO]' logging.basicConfig(format="%(message)s", level=logging.INFO) +def folders_to_tags(library:Library): + logging.info("Converting folders to Tags") + tree = dict(dirs={}) + def add_tag_to_tree(list:list[Tag]): + branch = tree + for tag in list: + if tag.name not in branch["dirs"]: + branch["dirs"][tag.name] = dict(dirs={},tag=tag) + branch = branch["dirs"][tag.name] + + def add_folders_to_tree(list:list[str])->Tag: + branch = tree + for folder in list: + if folder not in branch["dirs"]: + new_tag = Tag(-1, folder,"",[],([branch["tag"].id] if "tag" in branch else []),"") + library.add_tag_to_library(new_tag) + branch["dirs"][folder] = dict(dirs={},tag=new_tag) + branch = branch["dirs"][folder] + return branch["tag"] + + + for tag in library.tags: + reversed_tag = reverse_tag(library,tag,None) + add_tag_to_tree(reversed_tag) + + for entry in library.entries: + folders = entry.path.split("\\") + if len(folders)== 1 and folders[0]=="": continue + tag = add_folders_to_tree(folders) + if tag: + if not entry.has_tag(library,tag.id): + entry.add_tag(library,tag.id,6) + + logging.info("Done") + +def reverse_tag(library:Library,tag:Tag,list:list[Tag]) -> list[Tag]: + if list != None: + list.append(tag) + else: + list = [tag] + + if len(tag.subtag_ids) == 0: + list.reverse() + return list + else: + for subtag_id in tag.subtag_ids: + subtag = library.get_tag(subtag_id) + return reverse_tag(library,subtag,list) + +#=========== UI =========== + + class FoldersToTagsModal(QWidget): # done = Signal(int) def __init__(self, library:'Library', driver:'QtDriver'): @@ -68,14 +120,19 @@ class FoldersToTagsModal(QWidget): self.apply_button = QPushButton() self.apply_button.setText('&Apply') - self.apply_button.clicked.connect(lambda: self.folders_to_tags(self.library)) - + self.apply_button.mouseReleaseEvent = self.on_apply + self.showEvent = self.on_open self.root_layout.addWidget(self.desc_widget) self.root_layout.addWidget(self.scroll_area) self.root_layout.addWidget(self.apply_button) + def on_apply(self,event): + folders_to_tags(self.library) + self.close() + self.driver.preview_panel.update_widgets() + def on_open(self,event): for i in reversed(range(self.scroll_layout.count())): self.scroll_layout.itemAt(i).widget().setParent(None) @@ -83,10 +140,9 @@ class FoldersToTagsModal(QWidget): data = self.generate_preview_data(self.library) for folder in data["dirs"].values(): - test = self.TreeItemTest(folder,None) + test = TreeItem(folder,None) self.scroll_layout.addWidget(test) - def generate_preview_data(self,library:Library): tree = dict(dirs={},files=[]) @@ -107,10 +163,9 @@ class FoldersToTagsModal(QWidget): return branch for tag in library.tags: - reversed_tag = self.reverse_tag(tag,None) - logging.info(set(map(lambda tag:tag.name ,reversed_tag))) + reversed_tag = reverse_tag(library,tag,None) add_tag_to_tree(reversed_tag) - + for entry in library.entries: folders = entry.path.split("\\") if len(folders) == 1 and folders[0] == "": continue @@ -131,152 +186,99 @@ class FoldersToTagsModal(QWidget): def cut_branches_adding_nothing(branch:dict): folders = set(branch["dirs"].keys()) for folder in folders: - logging.info(folder) cut = cut_branches_adding_nothing(branch["dirs"][folder]) if cut: branch['dirs'].pop(folder) if not "tag" in branch: return - if branch["tag"].id == -1:#Needs to be first + if branch["tag"].id == -1 or len(branch["files"])>0:#Needs to be first return False if len(branch["dirs"].keys()) == 0: return True - cut_branches_adding_nothing(tree) return tree - def folders_to_tags(self,library:Library): - logging.info("Converting folders to Tags") - tree = dict(dirs={}) - def add_tag_to_tree(list:list[Tag]): - branch = tree - for tag in list: - if tag.name not in branch["dirs"]: - branch["dirs"][tag.name] = dict(dirs={},tag=tag) - branch = branch["dirs"][tag.name] +class ModifiedTagWidget(QWidget): # Needed to be modified because the original searched the display name in the library where it wasn't added yet + def __init__(self, tag:Tag,parentTag:Tag) -> None: + super().__init__() + self.tag = tag - def add_folders_to_tree(list:list[str])->Tag: - branch = tree - for folder in list: - if folder not in branch["dirs"]: - new_tag = Tag(-1, folder,"",[],([branch["tag"].id] if "tag" in branch else []),"") - library.add_tag_to_library(new_tag) - branch["dirs"][folder] = dict(dirs={},tag=new_tag) - branch = branch["dirs"][folder] - return branch["tag"] + self.setCursor(Qt.CursorShape.PointingHandCursor) + self.base_layout = QVBoxLayout(self) + self.base_layout.setObjectName('baseLayout') + self.base_layout.setContentsMargins(0, 0, 0, 0) + self.bg_button = QPushButton(self) + self.bg_button.setFlat(True) + if parentTag != None: + text = f"{tag.name} ({parentTag.name})".replace('&', '&&') + else: + text = tag.name.replace('&', '&&') + self.bg_button.setText(text) + self.bg_button.setContextMenuPolicy(Qt.ContextMenuPolicy.ActionsContextMenu) - for tag in library.tags: - reversed_tag = self.reverse_tag(tag,None) - add_tag_to_tree(reversed_tag) + self.inner_layout = QHBoxLayout() + self.inner_layout.setObjectName('innerLayout') + self.inner_layout.setContentsMargins(2, 2, 2, 2) + self.bg_button.setLayout(self.inner_layout) + self.bg_button.setMinimumSize(math.ceil(22*1.5), 22) - for entry in library.entries: - folders = entry.path.split("\\") - if len(folders)== 1 and folders[0]=="": continue - tag = add_folders_to_tree(folders) - if tag: - if not entry.has_tag(library,tag.id): - entry.add_tag(library,tag.id,6) + self.bg_button.setStyleSheet( + f'QPushButton{{' + f'background: {get_tag_color(ColorType.PRIMARY, tag.color)};' + f"color: {get_tag_color(ColorType.TEXT, tag.color)};" + f'font-weight: 600;' + f"border-color:{get_tag_color(ColorType.BORDER, tag.color)};" + f'border-radius: 6px;' + f'border-style:inset;' + f'border-width: {math.ceil(1*self.devicePixelRatio())}px;' + f'padding-right: 4px;' + f'padding-bottom: 1px;' + f'padding-left: 4px;' + f'font-size: 13px' + f'}}' + f'QPushButton::hover{{' + f"border-color:{get_tag_color(ColorType.LIGHT_ACCENT, tag.color)};" + f'}}') - self.close() - - logging.info("Done") - - def reverse_tag(self,tag:Tag,list:list[Tag]) -> list[Tag]: - if list != None: - list.append(tag) - else: - list = [tag] - - if len(tag.subtag_ids) == 0: - list.reverse() - return list - else: - for subtag_id in tag.subtag_ids: - subtag = self.library.get_tag(subtag_id) - return self.reverse_tag(subtag,list) - - class ModifiedTagWidget(QWidget): # Needed to be modified because the original searched the display name in the library where it wasn't added yet - def __init__(self, tag:Tag,parentTag:Tag) -> None: - super().__init__() - self.tag = tag - - self.setCursor(Qt.CursorShape.PointingHandCursor) - self.base_layout = QVBoxLayout(self) - self.base_layout.setObjectName('baseLayout') - self.base_layout.setContentsMargins(0, 0, 0, 0) - - self.bg_button = QPushButton(self) - self.bg_button.setFlat(True) - if parentTag != None: - text = f"{tag.name} ({parentTag.name})".replace('&', '&&') - else: - text = tag.name.replace('&', '&&') - self.bg_button.setText(text) - self.bg_button.setContextMenuPolicy(Qt.ContextMenuPolicy.ActionsContextMenu) - - self.inner_layout = QHBoxLayout() - self.inner_layout.setObjectName('innerLayout') - self.inner_layout.setContentsMargins(2, 2, 2, 2) - self.bg_button.setLayout(self.inner_layout) - self.bg_button.setMinimumSize(math.ceil(22*1.5), 22) - - self.bg_button.setStyleSheet( - f'QPushButton{{' - f'background: {get_tag_color(ColorType.PRIMARY, tag.color)};' - f"color: {get_tag_color(ColorType.TEXT, tag.color)};" - f'font-weight: 600;' - f"border-color:{get_tag_color(ColorType.BORDER, tag.color)};" - f'border-radius: 6px;' - f'border-style:inset;' - f'border-width: {math.ceil(1*self.devicePixelRatio())}px;' - f'padding-right: 4px;' - f'padding-bottom: 1px;' - f'padding-left: 4px;' - f'font-size: 13px' - f'}}' - f'QPushButton::hover{{' - f"border-color:{get_tag_color(ColorType.LIGHT_ACCENT, tag.color)};" - f'}}') - - self.base_layout.addWidget(self.bg_button) - self.setMinimumSize(50,20) - class TreeItemTest(QWidget): - def __init__(self,data:dict,parentTag:Tag): - super().__init__() - - self.root_layout = QVBoxLayout(self) - self.root_layout.setContentsMargins(20,0,0,0) - self.root_layout.setSpacing(1) - - self.test = QWidget() - self.root_layout.addWidget(self.test) - - self.tag_layout = FlowLayout(self.test) + self.base_layout.addWidget(self.bg_button) + self.setMinimumSize(50,20) +class TreeItem(QWidget): + def __init__(self,data:dict,parentTag:Tag): + super().__init__() - self.tag_widget = FoldersToTagsModal.ModifiedTagWidget(data["tag"],parentTag) - self.tag_widget.bg_button.clicked.connect(lambda:self.hide_show()) - self.tag_layout.addWidget(self.tag_widget) - - self.children_widget = QWidget() - self.children_layout = QVBoxLayout(self.children_widget) - self.root_layout.addWidget(self.children_widget) - - self.populate(data) - - def hide_show(self): - self.children_widget.setHidden(not self.children_widget.isHidden()) + self.root_layout = QVBoxLayout(self) + self.root_layout.setContentsMargins(20,0,0,0) + self.root_layout.setSpacing(1) - def populate(self,data:dict): - for folder in data["dirs"].values(): - item = FoldersToTagsModal.TreeItemTest(folder,data["tag"]) - self.children_layout.addWidget(item) - for file in data["files"]: - label = QLabel() - label.setText(file) - self.children_layout.addWidget(label) - - if len(data["files"]) == 0 and len(data["dirs"].values()) == 0: - self.hide_show() \ No newline at end of file + self.test = QWidget() + self.root_layout.addWidget(self.test) + + self.tag_layout = FlowLayout(self.test) + + self.tag_widget = ModifiedTagWidget(data["tag"],parentTag) + self.tag_widget.bg_button.clicked.connect(lambda:self.hide_show()) + self.tag_layout.addWidget(self.tag_widget) + + self.children_widget = QWidget() + self.children_layout = QVBoxLayout(self.children_widget) + self.root_layout.addWidget(self.children_widget) + + self.populate(data) + + def hide_show(self): + self.children_widget.setHidden(not self.children_widget.isHidden()) + + def populate(self,data:dict): + for folder in data["dirs"].values(): + item = TreeItem(folder,data["tag"]) + self.children_layout.addWidget(item) + for file in data["files"]: + label = QLabel() + label.setText(file) + self.children_layout.addWidget(label) + + if len(data["files"]) == 0 and len(data["dirs"].values()) == 0: + self.hide_show() \ No newline at end of file From b1c0ec58170c97c88b1082ef974df7dc7d5bc226 Mon Sep 17 00:00:00 2001 From: Creepler13 Date: Mon, 29 Apr 2024 23:01:15 +0200 Subject: [PATCH 2/2] Open/Close all buttons --- tagstudio/src/qt/modals/folders_to_tags.py | 247 ++++++++++++--------- 1 file changed, 142 insertions(+), 105 deletions(-) diff --git a/tagstudio/src/qt/modals/folders_to_tags.py b/tagstudio/src/qt/modals/folders_to_tags.py index 26b00e43..914a3189 100644 --- a/tagstudio/src/qt/modals/folders_to_tags.py +++ b/tagstudio/src/qt/modals/folders_to_tags.py @@ -77,73 +77,7 @@ def reverse_tag(library:Library,tag:Tag,list:list[Tag]) -> list[Tag]: #=========== UI =========== - -class FoldersToTagsModal(QWidget): - # done = Signal(int) - def __init__(self, library:'Library', driver:'QtDriver'): - super().__init__() - self.library = library - self.driver = driver - self.count = -1 - self.filename = '' - - self.setWindowTitle(f'Folders To Tags') - self.setWindowModality(Qt.WindowModality.ApplicationModal) - self.setMinimumSize(500, 800) - self.root_layout = QVBoxLayout(self) - self.root_layout.setContentsMargins(6,6,6,6) - - self.desc_widget = QLabel() - self.desc_widget.setObjectName('descriptionLabel') - self.desc_widget.setWordWrap(True) - self.desc_widget.setStyleSheet( - # 'background:blue;' - 'text-align:left;' - # 'font-weight:bold;' - 'font-size:18px;' - # 'padding-top: 6px' - '') - self.desc_widget.setText('''Creates tags based on the folder structure and applies them to entries.\n The Structure below shows all the tags that would be added and to which files they would be added. It being empty means that there are no Tag to be created or assigned''') - self.desc_widget.setAlignment(Qt.AlignmentFlag.AlignCenter) - - self.scroll_contents = QWidget() - self.scroll_layout = QVBoxLayout(self.scroll_contents) - self.scroll_layout.setContentsMargins(6,0,6,0) - self.scroll_layout.setAlignment(Qt.AlignmentFlag.AlignTop) - - self.scroll_area = QScrollArea() - self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn) - self.scroll_area.setWidgetResizable(True) - self.scroll_area.setFrameShadow(QFrame.Shadow.Plain) - self.scroll_area.setFrameShape(QFrame.Shape.NoFrame) - self.scroll_area.setWidget(self.scroll_contents) - - self.apply_button = QPushButton() - self.apply_button.setText('&Apply') - self.apply_button.mouseReleaseEvent = self.on_apply - - self.showEvent = self.on_open - - self.root_layout.addWidget(self.desc_widget) - self.root_layout.addWidget(self.scroll_area) - self.root_layout.addWidget(self.apply_button) - - def on_apply(self,event): - folders_to_tags(self.library) - self.close() - self.driver.preview_panel.update_widgets() - - def on_open(self,event): - for i in reversed(range(self.scroll_layout.count())): - self.scroll_layout.itemAt(i).widget().setParent(None) - - data = self.generate_preview_data(self.library) - - for folder in data["dirs"].values(): - test = TreeItem(folder,None) - self.scroll_layout.addWidget(test) - - def generate_preview_data(self,library:Library): +def generate_preview_data(library:Library): tree = dict(dirs={},files=[]) def add_tag_to_tree(list:list[Tag]): @@ -200,6 +134,146 @@ class FoldersToTagsModal(QWidget): return tree + +class FoldersToTagsModal(QWidget): + # done = Signal(int) + def __init__(self, library:'Library', driver:'QtDriver'): + super().__init__() + self.library = library + self.driver = driver + self.count = -1 + self.filename = '' + + self.setWindowTitle(f'Folders To Tags') + self.setWindowModality(Qt.WindowModality.ApplicationModal) + self.setMinimumSize(500, 800) + self.root_layout = QVBoxLayout(self) + self.root_layout.setContentsMargins(6,6,6,6) + + self.desc_widget = QLabel() + self.desc_widget.setObjectName('descriptionLabel') + self.desc_widget.setWordWrap(True) + self.desc_widget.setStyleSheet( + # 'background:blue;' + 'text-align:left;' + # 'font-weight:bold;' + 'font-size:18px;' + # 'padding-top: 6px' + '') + self.desc_widget.setText('''Creates tags based on the folder structure and applies them to entries.\n The Structure below shows all the tags that would be added and to which files they would be added. It being empty means that there are no Tag to be created or assigned''') + self.desc_widget.setAlignment(Qt.AlignmentFlag.AlignCenter) + + self.open_close_button_w = QWidget() + self.open_close_button_layout = QHBoxLayout(self.open_close_button_w) + + self.open_all_button = QPushButton() + self.open_all_button.setText("Open All") + self.open_all_button.clicked.connect(lambda:self.set_all_branches(False)) + self.close_all_button = QPushButton() + self.close_all_button.setText("Close All") + self.close_all_button.clicked.connect(lambda:self.set_all_branches(True)) + + self.open_close_button_layout.addWidget(self.open_all_button) + self.open_close_button_layout.addWidget(self.close_all_button) + + self.scroll_contents = QWidget() + self.scroll_layout = QVBoxLayout(self.scroll_contents) + self.scroll_layout.setContentsMargins(6,0,6,0) + self.scroll_layout.setAlignment(Qt.AlignmentFlag.AlignTop) + + self.scroll_area = QScrollArea() + self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn) + self.scroll_area.setWidgetResizable(True) + self.scroll_area.setFrameShadow(QFrame.Shadow.Plain) + self.scroll_area.setFrameShape(QFrame.Shape.NoFrame) + self.scroll_area.setWidget(self.scroll_contents) + + self.apply_button = QPushButton() + self.apply_button.setText('&Apply') + self.apply_button.clicked.connect(self.on_apply) + + self.showEvent = self.on_open + + self.root_layout.addWidget(self.desc_widget) + self.root_layout.addWidget(self.open_close_button_w) + self.root_layout.addWidget(self.scroll_area) + self.root_layout.addWidget(self.apply_button) + + def on_apply(self,event): + folders_to_tags(self.library) + self.close() + self.driver.preview_panel.update_widgets() + + def on_open(self,event): + for i in reversed(range(self.scroll_layout.count())): + self.scroll_layout.itemAt(i).widget().setParent(None) + + data = generate_preview_data(self.library) + + for folder in data["dirs"].values(): + test = TreeItem(folder,None) + self.scroll_layout.addWidget(test) + + def set_all_branches(self,hidden:bool): + for i in reversed(range(self.scroll_layout.count())): + child = self.scroll_layout.itemAt(i).widget() + if type(child) == TreeItem: + child.set_all_branches(hidden) + +class TreeItem(QWidget): + def __init__(self,data:dict,parentTag:Tag): + super().__init__() + + self.setStyleSheet("QLabel{font-size: 13px}") + + self.root_layout = QVBoxLayout(self) + self.root_layout.setContentsMargins(20,0,0,0) + self.root_layout.setSpacing(1) + + self.test = QWidget() + self.root_layout.addWidget(self.test) + + self.tag_layout = FlowLayout(self.test) + + self.label = QLabel() + self.tag_layout.addWidget(self.label) + self.tag_widget = ModifiedTagWidget(data["tag"],parentTag) + self.tag_widget.bg_button.clicked.connect(lambda:self.hide_show()) + self.tag_layout.addWidget(self.tag_widget) + + self.children_widget = QWidget() + self.children_layout = QVBoxLayout(self.children_widget) + self.root_layout.addWidget(self.children_widget) + + self.populate(data) + + def hide_show(self): + self.children_widget.setHidden(not self.children_widget.isHidden()) + self.label.setText(">" if self.children_widget.isHidden() else "v") + + def populate(self,data:dict): + for folder in data["dirs"].values(): + item = TreeItem(folder,data["tag"]) + self.children_layout.addWidget(item) + for file in data["files"]: + label = QLabel() + label.setText(" -> "+file) + self.children_layout.addWidget(label) + + if len(data["files"]) == 0 and len(data["dirs"].values()) == 0: + self.hide_show() + else: + self.label.setText("v") + + def set_all_branches(self,hidden:bool): + for i in reversed(range(self.children_layout.count())): + child = self.children_layout.itemAt(i).widget() + if type(child) == TreeItem: + child.set_all_branches(hidden) + + self.children_widget.setHidden(hidden) + self.label.setText(">" if self.children_widget.isHidden() else "v") + class ModifiedTagWidget(QWidget): # Needed to be modified because the original searched the display name in the library where it wasn't added yet def __init__(self, tag:Tag,parentTag:Tag) -> None: super().__init__() @@ -244,41 +318,4 @@ class ModifiedTagWidget(QWidget): # Needed to be modified because the original s f'}}') self.base_layout.addWidget(self.bg_button) - self.setMinimumSize(50,20) -class TreeItem(QWidget): - def __init__(self,data:dict,parentTag:Tag): - super().__init__() - - self.root_layout = QVBoxLayout(self) - self.root_layout.setContentsMargins(20,0,0,0) - self.root_layout.setSpacing(1) - - self.test = QWidget() - self.root_layout.addWidget(self.test) - - self.tag_layout = FlowLayout(self.test) - - self.tag_widget = ModifiedTagWidget(data["tag"],parentTag) - self.tag_widget.bg_button.clicked.connect(lambda:self.hide_show()) - self.tag_layout.addWidget(self.tag_widget) - - self.children_widget = QWidget() - self.children_layout = QVBoxLayout(self.children_widget) - self.root_layout.addWidget(self.children_widget) - - self.populate(data) - - def hide_show(self): - self.children_widget.setHidden(not self.children_widget.isHidden()) - - def populate(self,data:dict): - for folder in data["dirs"].values(): - item = TreeItem(folder,data["tag"]) - self.children_layout.addWidget(item) - for file in data["files"]: - label = QLabel() - label.setText(file) - self.children_layout.addWidget(label) - - if len(data["files"]) == 0 and len(data["dirs"].values()) == 0: - self.hide_show() \ No newline at end of file + self.setMinimumSize(50,20) \ No newline at end of file