mirror of
https://github.com/TagStudioDev/TagStudio.git
synced 2024-07-30 21:27:34 +00:00
Split out remaining modals from ts_qt.py
This commit is contained in:
parent
f7c4e1ccc0
commit
ca9735ca86
11 changed files with 102 additions and 30852 deletions
|
@ -1,2 +1,4 @@
|
|||
from .open_file import open_file
|
||||
from .file_opener import FileOpenerHelper, FileOpenerLabel
|
||||
from .function_iterator import FunctionIterator
|
||||
from .custom_runnable import CustomRunnable
|
19
tagstudio/src/qt/helpers/custom_runnable.py
Normal file
19
tagstudio/src/qt/helpers/custom_runnable.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Copyright (C) 2024 Travis Abendshien (CyanVoxel).
|
||||
# Licensed under the GPL-3.0 License.
|
||||
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
|
||||
|
||||
|
||||
from PySide6.QtCore import Signal, QRunnable, QObject
|
||||
|
||||
|
||||
class CustomRunnable(QRunnable, QObject):
|
||||
done = Signal()
|
||||
|
||||
def __init__(self, function) -> None:
|
||||
QRunnable.__init__(self)
|
||||
QObject.__init__(self)
|
||||
self.function = function
|
||||
|
||||
def run(self):
|
||||
self.function()
|
||||
self.done.emit()
|
21
tagstudio/src/qt/helpers/function_iterator.py
Normal file
21
tagstudio/src/qt/helpers/function_iterator.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Copyright (C) 2024 Travis Abendshien (CyanVoxel).
|
||||
# Licensed under the GPL-3.0 License.
|
||||
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
|
||||
|
||||
|
||||
from types import FunctionType
|
||||
|
||||
from PySide6.QtCore import Signal, QObject
|
||||
|
||||
|
||||
class FunctionIterator(QObject):
|
||||
"""Iterates over a yielding function and emits progress as the 'value' signal.\n\nThread-Safe Guarantee™"""
|
||||
value = Signal(object)
|
||||
|
||||
def __init__(self, function: FunctionType):
|
||||
super().__init__()
|
||||
self.iterable = function
|
||||
|
||||
def run(self):
|
||||
for i in self.iterable():
|
||||
self.value.emit(i)
|
|
@ -3,3 +3,9 @@ from .build_tag import BuildTagPanel
|
|||
from .tag_database import TagDatabasePanel
|
||||
from .add_field import AddFieldModal
|
||||
from .file_extension import FileExtensionModal
|
||||
from .delete_unlinked import DeleteUnlinkedEntriesModal
|
||||
from .relink_unlinked import RelinkUnlinkedEntries
|
||||
from .fix_unlinked import FixUnlinkedEntriesModal
|
||||
from .mirror_entities import MirrorEntriesModal
|
||||
from .fix_dupes import FixDupeFilesModal
|
||||
from .folders_to_tags import FoldersToTagsModal
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -17,23 +17,21 @@ from types import FunctionType
|
|||
from datetime import datetime as dt
|
||||
from pathlib import Path
|
||||
from queue import Empty, Queue
|
||||
from time import sleep
|
||||
from typing import Optional
|
||||
|
||||
import cv2
|
||||
from PIL import Image, UnidentifiedImageError, ImageQt
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtCore import QObject, QThread, Signal, QRunnable, Qt, QThreadPool, QSize, QEvent, QTimer, QSettings
|
||||
from PySide6.QtCore import QObject, QThread, Signal, Qt, QThreadPool, QSize, QEvent, QTimer, QSettings
|
||||
from PySide6.QtGui import (QGuiApplication, QPixmap, QEnterEvent, QMouseEvent, QResizeEvent, QColor, QAction,
|
||||
QStandardItemModel, QStandardItem, QFontDatabase, QIcon)
|
||||
QFontDatabase, QIcon)
|
||||
from PySide6.QtUiTools import QUiLoader
|
||||
from PySide6.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QLineEdit,
|
||||
QScrollArea, QFrame, QFileDialog, QListView, QSplitter, QSizePolicy, QMessageBox,
|
||||
QScrollArea, QFrame, QFileDialog, QSplitter, QSizePolicy, QMessageBox,
|
||||
QBoxLayout, QCheckBox, QSplashScreen, QMenu)
|
||||
from humanfriendly import format_timespan, format_size
|
||||
|
||||
from src.core.library import Collation, Entry, ItemType, Library, Tag
|
||||
from src.core.palette import ColorType, get_tag_color
|
||||
from src.core.library import Entry, ItemType, Library
|
||||
from src.core.ts_core import (PLAINTEXT_TYPES, TagStudioCore, TAG_COLORS, DATE_FIELDS, TEXT_FIELDS, BOX_FIELDS, ALL_FILE_TYPES,
|
||||
SHORTCUT_TYPES, PROGRAM_TYPES, ARCHIVE_TYPES, PRESENTATION_TYPES,
|
||||
SPREADSHEET_TYPES, DOC_TYPES, AUDIO_TYPES, VIDEO_TYPES, IMAGE_TYPES,
|
||||
|
@ -42,10 +40,11 @@ from src.core.ts_core import (PLAINTEXT_TYPES, TagStudioCore, TAG_COLORS, DATE_F
|
|||
from src.core.utils.web import strip_web_protocol
|
||||
from src.qt.flowlayout import FlowLayout, FlowWidget
|
||||
from src.qt.main_window import Ui_MainWindow
|
||||
from src.qt.helpers import open_file, FileOpenerHelper, FileOpenerLabel
|
||||
from src.qt.widgets import (FieldContainer, FieldWidget, CollageIconRenderer, ThumbButton, ThumbRenderer, PanelModal,
|
||||
EditTextBox, EditTextLine, ProgressWidget, TagBoxWidget, TextWidget)
|
||||
from src.qt.modals import BuildTagPanel, TagDatabasePanel, AddFieldModal, FileExtensionModal
|
||||
from src.qt.helpers import open_file, FileOpenerHelper, FileOpenerLabel, FunctionIterator, CustomRunnable
|
||||
from src.qt.widgets import (FieldContainer, CollageIconRenderer, ThumbButton, ThumbRenderer, PanelModal, EditTextBox,
|
||||
EditTextLine, ProgressWidget, TagBoxWidget, TextWidget)
|
||||
from src.qt.modals import (BuildTagPanel, TagDatabasePanel, AddFieldModal, FileExtensionModal, FixUnlinkedEntriesModal,
|
||||
FixDupeFilesModal, FoldersToTagsModal)
|
||||
import src.qt.resources_rc
|
||||
|
||||
# SIGQUIT is not defined on Windows
|
||||
|
@ -107,608 +106,6 @@ class Consumer(QThread):
|
|||
pass
|
||||
|
||||
|
||||
class FunctionIterator(QObject):
|
||||
"""Iterates over a yielding function and emits progress as the 'value' signal.\n\nThread-Safe Guarantee™"""
|
||||
value = Signal(object)
|
||||
def __init__(self, function: FunctionType):
|
||||
super().__init__()
|
||||
self.iterable = function
|
||||
|
||||
def run(self):
|
||||
for i in self.iterable():
|
||||
self.value.emit(i)
|
||||
|
||||
|
||||
class FixDupeFilesModal(QWidget):
|
||||
# done = Signal(int)
|
||||
def __init__(self, library:'Library', driver:'QtDriver'):
|
||||
super().__init__()
|
||||
self.lib = library
|
||||
self.driver:QtDriver = driver
|
||||
self.count = -1
|
||||
self.filename = ''
|
||||
self.setWindowTitle(f'Fix Duplicate Files')
|
||||
self.setWindowModality(Qt.WindowModality.ApplicationModal)
|
||||
self.setMinimumSize(400, 300)
|
||||
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:14px;'
|
||||
# 'padding-top: 6px'
|
||||
'')
|
||||
self.desc_widget.setText('''TagStudio supports importing DupeGuru results to manage duplicate files.''')
|
||||
self.desc_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.dupe_count = QLabel()
|
||||
self.dupe_count.setObjectName('dupeCountLabel')
|
||||
self.dupe_count.setStyleSheet(
|
||||
# 'background:blue;'
|
||||
# 'text-align:center;'
|
||||
'font-weight:bold;'
|
||||
'font-size:14px;'
|
||||
# 'padding-top: 6px'
|
||||
'')
|
||||
self.dupe_count.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.file_label = QLabel()
|
||||
self.file_label.setObjectName('fileLabel')
|
||||
# self.file_label.setStyleSheet(
|
||||
# # 'background:blue;'
|
||||
# # 'text-align:center;'
|
||||
# 'font-weight:bold;'
|
||||
# 'font-size:14px;'
|
||||
# # 'padding-top: 6px'
|
||||
# '')
|
||||
# self.file_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.file_label.setText('No DupeGuru File Selected')
|
||||
|
||||
|
||||
self.open_button = QPushButton()
|
||||
self.open_button.setText('&Load DupeGuru File')
|
||||
self.open_button.clicked.connect(lambda: self.select_file())
|
||||
|
||||
self.mirror_button = QPushButton()
|
||||
self.mirror_modal = MirrorEntriesModal(self.lib, self.driver)
|
||||
self.mirror_modal.done.connect(lambda: self.refresh_dupes())
|
||||
self.mirror_button.setText('&Mirror Entries')
|
||||
self.mirror_button.clicked.connect(lambda: self.mirror_modal.show())
|
||||
self.mirror_desc = QLabel()
|
||||
self.mirror_desc.setWordWrap(True)
|
||||
self.mirror_desc.setText("""Mirror the Entry data across each duplicate match set, combining all data while not removing or duplicating fields. This operation will not delete any files or data.""")
|
||||
|
||||
# self.mirror_delete_button = QPushButton()
|
||||
# self.mirror_delete_button.setText('Mirror && Delete')
|
||||
|
||||
self.advice_label = QLabel()
|
||||
self.advice_label.setWordWrap(True)
|
||||
self.advice_label.setText("""After mirroring, you're free to use DupeGuru to delete the unwanted files. Afterwards, use TagStudio's "Fix Unlinked Entries" feature in the Tools menu in order to delete the unlinked Entries.""")
|
||||
|
||||
self.button_container = QWidget()
|
||||
self.button_layout = QHBoxLayout(self.button_container)
|
||||
self.button_layout.setContentsMargins(6,6,6,6)
|
||||
self.button_layout.addStretch(1)
|
||||
|
||||
self.done_button = QPushButton()
|
||||
self.done_button.setText('&Done')
|
||||
# self.save_button.setAutoDefault(True)
|
||||
self.done_button.setDefault(True)
|
||||
self.done_button.clicked.connect(self.hide)
|
||||
# self.done_button.clicked.connect(lambda: self.done.emit(self.combo_box.currentIndex()))
|
||||
# self.save_button.clicked.connect(lambda: save_callback(widget.get_content()))
|
||||
self.button_layout.addWidget(self.done_button)
|
||||
|
||||
# self.returnPressed.connect(lambda: self.done.emit(self.combo_box.currentIndex()))
|
||||
|
||||
# self.done.connect(lambda x: callback(x))
|
||||
|
||||
self.root_layout.addWidget(self.desc_widget)
|
||||
self.root_layout.addWidget(self.dupe_count)
|
||||
self.root_layout.addWidget(self.file_label)
|
||||
self.root_layout.addWidget(self.open_button)
|
||||
# self.mirror_delete_button.setHidden(True)
|
||||
|
||||
self.root_layout.addWidget(self.mirror_button)
|
||||
self.root_layout.addWidget(self.mirror_desc)
|
||||
# self.root_layout.addWidget(self.mirror_delete_button)
|
||||
self.root_layout.addWidget(self.advice_label)
|
||||
# self.root_layout.setStretch(1,2)
|
||||
self.root_layout.addStretch(1)
|
||||
self.root_layout.addWidget(self.button_container)
|
||||
|
||||
self.set_dupe_count(self.count)
|
||||
|
||||
def select_file(self):
|
||||
qfd = QFileDialog(self,
|
||||
'Open DupeGuru Results File',
|
||||
os.path.normpath(self.lib.library_dir))
|
||||
qfd.setFileMode(QFileDialog.FileMode.ExistingFile)
|
||||
qfd.setNameFilter("DupeGuru Files (*.dupeguru)")
|
||||
if qfd.exec_():
|
||||
filename = qfd.selectedFiles()
|
||||
if filename:
|
||||
self.set_filename(filename[0])
|
||||
|
||||
def set_filename(self, filename:str):
|
||||
if filename:
|
||||
self.file_label.setText(filename)
|
||||
else:
|
||||
self.file_label.setText('No DupeGuru File Selected')
|
||||
self.filename = filename
|
||||
self.refresh_dupes()
|
||||
self.mirror_modal.refresh_list()
|
||||
|
||||
def refresh_dupes(self):
|
||||
self.lib.refresh_dupe_files(self.filename)
|
||||
self.set_dupe_count(len(self.lib.dupe_files))
|
||||
|
||||
def set_dupe_count(self, count:int):
|
||||
self.count = count
|
||||
if self.count < 0:
|
||||
self.mirror_button.setDisabled(True)
|
||||
self.dupe_count.setText(f'Duplicate File Matches: N/A')
|
||||
elif self.count == 0:
|
||||
self.mirror_button.setDisabled(True)
|
||||
self.dupe_count.setText(f'Duplicate File Matches: {count}')
|
||||
else:
|
||||
self.mirror_button.setDisabled(False)
|
||||
self.dupe_count.setText(f'Duplicate File Matches: {count}')
|
||||
|
||||
|
||||
class MirrorEntriesModal(QWidget):
|
||||
done = Signal()
|
||||
def __init__(self, library:'Library', driver:'QtDriver'):
|
||||
super().__init__()
|
||||
self.lib = library
|
||||
self.driver:QtDriver = driver
|
||||
self.setWindowTitle(f'Mirror Entries')
|
||||
self.setWindowModality(Qt.WindowModality.ApplicationModal)
|
||||
self.setMinimumSize(500, 400)
|
||||
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.setText(f'''
|
||||
Are you sure you want to mirror the following {len(self.lib.dupe_files)} Entries?
|
||||
''')
|
||||
self.desc_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.list_view = QListView()
|
||||
self.model = QStandardItemModel()
|
||||
self.list_view.setModel(self.model)
|
||||
|
||||
self.button_container = QWidget()
|
||||
self.button_layout = QHBoxLayout(self.button_container)
|
||||
self.button_layout.setContentsMargins(6,6,6,6)
|
||||
self.button_layout.addStretch(1)
|
||||
|
||||
self.cancel_button = QPushButton()
|
||||
self.cancel_button.setText('&Cancel')
|
||||
self.cancel_button.setDefault(True)
|
||||
self.cancel_button.clicked.connect(self.hide)
|
||||
self.button_layout.addWidget(self.cancel_button)
|
||||
|
||||
self.mirror_button = QPushButton()
|
||||
self.mirror_button.setText('&Mirror')
|
||||
self.mirror_button.clicked.connect(self.hide)
|
||||
self.mirror_button.clicked.connect(lambda: self.mirror_entries())
|
||||
self.button_layout.addWidget(self.mirror_button)
|
||||
|
||||
self.root_layout.addWidget(self.desc_widget)
|
||||
self.root_layout.addWidget(self.list_view)
|
||||
self.root_layout.addWidget(self.button_container)
|
||||
|
||||
def refresh_list(self):
|
||||
self.desc_widget.setText(f'''
|
||||
Are you sure you want to mirror the following {len(self.lib.dupe_files)} Entries?
|
||||
''')
|
||||
|
||||
self.model.clear()
|
||||
for i in self.lib.dupe_files:
|
||||
self.model.appendRow(QStandardItem(str(i)))
|
||||
|
||||
def mirror_entries(self):
|
||||
# pb = QProgressDialog('', None, 0, len(self.lib.dupe_files))
|
||||
# # pb.setMaximum(len(self.lib.missing_files))
|
||||
# pb.setFixedSize(432, 112)
|
||||
# pb.setWindowFlags(pb.windowFlags() & ~Qt.WindowType.WindowCloseButtonHint)
|
||||
# pb.setWindowTitle('Mirroring Entries')
|
||||
# pb.setWindowModality(Qt.WindowModality.ApplicationModal)
|
||||
# pb.show()
|
||||
|
||||
# r = CustomRunnable(lambda: self.mirror_entries_runnable(pb))
|
||||
# r.done.connect(lambda: self.done.emit())
|
||||
# r.done.connect(lambda: self.driver.preview_panel.refresh())
|
||||
# # r.done.connect(lambda: self.model.clear())
|
||||
# # QThreadPool.globalInstance().start(r)
|
||||
# r.run()
|
||||
|
||||
iterator = FunctionIterator(self.mirror_entries_runnable)
|
||||
pw = ProgressWidget(
|
||||
window_title='Mirroring Entries',
|
||||
label_text=f'Mirroring 1/{len(self.lib.dupe_files)} Entries...',
|
||||
cancel_button_text=None,
|
||||
minimum=0,
|
||||
maximum=len(self.lib.dupe_files)
|
||||
)
|
||||
pw.show()
|
||||
iterator.value.connect(lambda x: pw.update_progress(x+1))
|
||||
iterator.value.connect(lambda x: pw.update_label(f'Mirroring {x+1}/{len(self.lib.dupe_files)} Entries...'))
|
||||
r = CustomRunnable(lambda:iterator.run())
|
||||
QThreadPool.globalInstance().start(r)
|
||||
r.done.connect(lambda: (
|
||||
pw.hide(),
|
||||
pw.deleteLater(),
|
||||
self.driver.preview_panel.update_widgets(),
|
||||
self.done.emit()
|
||||
))
|
||||
|
||||
def mirror_entries_runnable(self):
|
||||
mirrored = []
|
||||
for i, dupe in enumerate(self.lib.dupe_files):
|
||||
# pb.setValue(i)
|
||||
# pb.setLabelText(f'Mirroring {i}/{len(self.lib.dupe_files)} Entries')
|
||||
entry_id_1 = self.lib.get_entry_id_from_filepath(
|
||||
dupe[0])
|
||||
entry_id_2 = self.lib.get_entry_id_from_filepath(
|
||||
dupe[1])
|
||||
self.lib.mirror_entry_fields([entry_id_1, entry_id_2])
|
||||
sleep(0.005)
|
||||
yield i
|
||||
for d in mirrored:
|
||||
self.lib.dupe_files.remove(d)
|
||||
# self.driver.filter_items('')
|
||||
# self.done.emit()
|
||||
|
||||
|
||||
class FixUnlinkedEntriesModal(QWidget):
|
||||
# done = Signal(int)
|
||||
def __init__(self, library:'Library', driver:'QtDriver'):
|
||||
super().__init__()
|
||||
self.lib = library
|
||||
self.driver:QtDriver = driver
|
||||
self.count = -1
|
||||
self.setWindowTitle(f'Fix Unlinked Entries')
|
||||
self.setWindowModality(Qt.WindowModality.ApplicationModal)
|
||||
self.setMinimumSize(400, 300)
|
||||
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:14px;'
|
||||
# 'padding-top: 6px'
|
||||
'')
|
||||
self.desc_widget.setText('''Each library entry is linked to a file in one of your directories. If a file linked to an entry is moved or deleted outside of TagStudio, it is then considered unlinked.
|
||||
Unlinked entries may be automatically relinked via searching your directories, manually relinked by the user, or deleted if desired.''')
|
||||
self.desc_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.missing_count = QLabel()
|
||||
self.missing_count.setObjectName('missingCountLabel')
|
||||
self.missing_count.setStyleSheet(
|
||||
# 'background:blue;'
|
||||
# 'text-align:center;'
|
||||
'font-weight:bold;'
|
||||
'font-size:14px;'
|
||||
# 'padding-top: 6px'
|
||||
'')
|
||||
self.missing_count.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
# self.missing_count.setText('Missing Files: N/A')
|
||||
|
||||
|
||||
self.refresh_button = QPushButton()
|
||||
self.refresh_button.setText('&Refresh')
|
||||
self.refresh_button.clicked.connect(lambda: self.refresh_missing_files())
|
||||
|
||||
self.search_button = QPushButton()
|
||||
self.search_button.setText('&Search && Relink')
|
||||
self.relink_class = RelinkUnlinkedEntries(self.lib, self.driver)
|
||||
self.relink_class.done.connect(lambda: self.refresh_missing_files())
|
||||
self.relink_class.done.connect(lambda: self.driver.update_thumbs())
|
||||
self.search_button.clicked.connect(lambda: self.relink_class.repair_entries())
|
||||
|
||||
self.manual_button = QPushButton()
|
||||
self.manual_button.setText('&Manual Relink')
|
||||
|
||||
self.delete_button = QPushButton()
|
||||
self.delete_modal = DeleteUnlinkedEntriesModal(self.lib, self.driver)
|
||||
self.delete_modal.done.connect(lambda: self.set_missing_count(len(self.lib.missing_files)))
|
||||
self.delete_modal.done.connect(lambda: self.driver.update_thumbs())
|
||||
self.delete_button.setText('De&lete Unlinked Entries')
|
||||
self.delete_button.clicked.connect(lambda: self.delete_modal.show())
|
||||
|
||||
# self.combo_box = QComboBox()
|
||||
# self.combo_box.setEditable(False)
|
||||
# # self.combo_box.setMaxVisibleItems(5)
|
||||
# self.combo_box.setStyleSheet('combobox-popup:0;')
|
||||
# self.combo_box.view().setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||
# for df in self.lib.default_fields:
|
||||
# self.combo_box.addItem(f'{df["name"]} ({df["type"].replace("_", " ").title()})')
|
||||
|
||||
|
||||
self.button_container = QWidget()
|
||||
self.button_layout = QHBoxLayout(self.button_container)
|
||||
self.button_layout.setContentsMargins(6,6,6,6)
|
||||
self.button_layout.addStretch(1)
|
||||
|
||||
self.done_button = QPushButton()
|
||||
self.done_button.setText('&Done')
|
||||
# self.save_button.setAutoDefault(True)
|
||||
self.done_button.setDefault(True)
|
||||
self.done_button.clicked.connect(self.hide)
|
||||
# self.done_button.clicked.connect(lambda: self.done.emit(self.combo_box.currentIndex()))
|
||||
# self.save_button.clicked.connect(lambda: save_callback(widget.get_content()))
|
||||
self.button_layout.addWidget(self.done_button)
|
||||
|
||||
# self.returnPressed.connect(lambda: self.done.emit(self.combo_box.currentIndex()))
|
||||
|
||||
# self.done.connect(lambda x: callback(x))
|
||||
|
||||
self.root_layout.addWidget(self.desc_widget)
|
||||
self.root_layout.addWidget(self.missing_count)
|
||||
self.root_layout.addWidget(self.refresh_button)
|
||||
self.root_layout.addWidget(self.search_button)
|
||||
self.manual_button.setHidden(True)
|
||||
self.root_layout.addWidget(self.manual_button)
|
||||
self.root_layout.addWidget(self.delete_button)
|
||||
# self.root_layout.setStretch(1,2)
|
||||
self.root_layout.addStretch(1)
|
||||
self.root_layout.addWidget(self.button_container)
|
||||
|
||||
self.set_missing_count(self.count)
|
||||
|
||||
def refresh_missing_files(self):
|
||||
logging.info(f'Start RMF: {QThread.currentThread()}')
|
||||
# pb = QProgressDialog(f'Scanning Library for Unlinked Entries...', None, 0,len(self.lib.entries))
|
||||
# pb.setFixedSize(432, 112)
|
||||
# pb.setWindowFlags(pb.windowFlags() & ~Qt.WindowType.WindowCloseButtonHint)
|
||||
# pb.setWindowTitle('Scanning Library')
|
||||
# pb.setWindowModality(Qt.WindowModality.ApplicationModal)
|
||||
# pb.show()
|
||||
|
||||
iterator = FunctionIterator(self.lib.refresh_missing_files)
|
||||
pw = ProgressWidget(
|
||||
window_title='Scanning Library',
|
||||
label_text=f'Scanning Library for Unlinked Entries...',
|
||||
cancel_button_text=None,
|
||||
minimum=0,
|
||||
maximum=len(self.lib.entries)
|
||||
)
|
||||
pw.show()
|
||||
iterator.value.connect(lambda v: pw.update_progress(v+1))
|
||||
# rmf.value.connect(lambda v: pw.update_label(f'Progress: {v}'))
|
||||
r = CustomRunnable(lambda:iterator.run())
|
||||
QThreadPool.globalInstance().start(r)
|
||||
r.done.connect(lambda: (pw.hide(), pw.deleteLater(),
|
||||
self.set_missing_count(len(self.lib.missing_files)),
|
||||
self.delete_modal.refresh_list()))
|
||||
|
||||
# r = CustomRunnable(lambda: self.lib.refresh_missing_files(lambda v: self.update_scan_value(pb, v)))
|
||||
# r.done.connect(lambda: (pb.hide(), pb.deleteLater(), self.set_missing_count(len(self.lib.missing_files)), self.delete_modal.refresh_list()))
|
||||
# QThreadPool.globalInstance().start(r)
|
||||
# # r.run()
|
||||
# pass
|
||||
|
||||
# def update_scan_value(self, pb:QProgressDialog, value=int):
|
||||
# # pb.setLabelText(f'Scanning Library for Unlinked Entries ({value}/{len(self.lib.entries)})...')
|
||||
# pb.setValue(value)
|
||||
|
||||
def set_missing_count(self, count:int):
|
||||
self.count = count
|
||||
if self.count < 0:
|
||||
self.search_button.setDisabled(True)
|
||||
self.delete_button.setDisabled(True)
|
||||
self.missing_count.setText(f'Unlinked Entries: N/A')
|
||||
elif self.count == 0:
|
||||
self.search_button.setDisabled(True)
|
||||
self.delete_button.setDisabled(True)
|
||||
self.missing_count.setText(f'Unlinked Entries: {count}')
|
||||
else:
|
||||
self.search_button.setDisabled(False)
|
||||
self.delete_button.setDisabled(False)
|
||||
self.missing_count.setText(f'Unlinked Entries: {count}')
|
||||
|
||||
|
||||
class DeleteUnlinkedEntriesModal(QWidget):
|
||||
done = Signal()
|
||||
def __init__(self, library:'Library', driver:'QtDriver'):
|
||||
super().__init__()
|
||||
self.lib = library
|
||||
self.driver:QtDriver = driver
|
||||
self.setWindowTitle(f'Delete Unlinked Entries')
|
||||
self.setWindowModality(Qt.WindowModality.ApplicationModal)
|
||||
self.setMinimumSize(500, 400)
|
||||
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.setText(f'''
|
||||
Are you sure you want to delete the following {len(self.lib.missing_files)} entries?
|
||||
''')
|
||||
self.desc_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.list_view = QListView()
|
||||
self.model = QStandardItemModel()
|
||||
self.list_view.setModel(self.model)
|
||||
|
||||
self.button_container = QWidget()
|
||||
self.button_layout = QHBoxLayout(self.button_container)
|
||||
self.button_layout.setContentsMargins(6,6,6,6)
|
||||
self.button_layout.addStretch(1)
|
||||
|
||||
self.cancel_button = QPushButton()
|
||||
self.cancel_button.setText('&Cancel')
|
||||
self.cancel_button.setDefault(True)
|
||||
self.cancel_button.clicked.connect(self.hide)
|
||||
self.button_layout.addWidget(self.cancel_button)
|
||||
|
||||
self.delete_button = QPushButton()
|
||||
self.delete_button.setText('&Delete')
|
||||
self.delete_button.clicked.connect(self.hide)
|
||||
self.delete_button.clicked.connect(lambda: self.delete_entries())
|
||||
self.button_layout.addWidget(self.delete_button)
|
||||
|
||||
self.root_layout.addWidget(self.desc_widget)
|
||||
self.root_layout.addWidget(self.list_view)
|
||||
self.root_layout.addWidget(self.button_container)
|
||||
|
||||
def refresh_list(self):
|
||||
self.desc_widget.setText(f'''
|
||||
Are you sure you want to delete the following {len(self.lib.missing_files)} entries?
|
||||
''')
|
||||
|
||||
self.model.clear()
|
||||
for i in self.lib.missing_files:
|
||||
self.model.appendRow(QStandardItem(i))
|
||||
|
||||
def delete_entries(self):
|
||||
# pb = QProgressDialog('', None, 0, len(self.lib.missing_files))
|
||||
# # pb.setMaximum(len(self.lib.missing_files))
|
||||
# pb.setFixedSize(432, 112)
|
||||
# pb.setWindowFlags(pb.windowFlags() & ~Qt.WindowType.WindowCloseButtonHint)
|
||||
# pb.setWindowTitle('Deleting Entries')
|
||||
# pb.setWindowModality(Qt.WindowModality.ApplicationModal)
|
||||
# pb.show()
|
||||
|
||||
# r = CustomRunnable(lambda: self.lib.ref(pb))
|
||||
# r.done.connect(lambda: self.done.emit())
|
||||
# # r.done.connect(lambda: self.model.clear())
|
||||
# QThreadPool.globalInstance().start(r)
|
||||
# # r.run()
|
||||
|
||||
|
||||
|
||||
iterator = FunctionIterator(self.lib.remove_missing_files)
|
||||
|
||||
pw = ProgressWidget(
|
||||
window_title='Deleting Entries',
|
||||
label_text='',
|
||||
cancel_button_text=None,
|
||||
minimum=0,
|
||||
maximum=len(self.lib.missing_files)
|
||||
)
|
||||
pw.show()
|
||||
|
||||
iterator.value.connect(lambda x: pw.update_progress(x[0]+1))
|
||||
iterator.value.connect(lambda x: pw.update_label(f'Deleting {x[0]+1}/{len(self.lib.missing_files)} Unlinked Entries'))
|
||||
iterator.value.connect(lambda x: self.driver.purge_item_from_navigation(ItemType.ENTRY, x[1]))
|
||||
|
||||
r = CustomRunnable(lambda:iterator.run())
|
||||
QThreadPool.globalInstance().start(r)
|
||||
r.done.connect(lambda: (pw.hide(), pw.deleteLater(), self.done.emit()))
|
||||
|
||||
# def delete_entries_runnable(self):
|
||||
# deleted = []
|
||||
# for i, missing in enumerate(self.lib.missing_files):
|
||||
# # pb.setValue(i)
|
||||
# # pb.setLabelText(f'Deleting {i}/{len(self.lib.missing_files)} Unlinked Entries')
|
||||
# try:
|
||||
# id = self.lib.get_entry_id_from_filepath(missing)
|
||||
# logging.info(f'Removing Entry ID {id}:\n\t{missing}')
|
||||
# self.lib.remove_entry(id)
|
||||
# self.driver.purge_item_from_navigation(ItemType.ENTRY, id)
|
||||
# deleted.append(missing)
|
||||
# except KeyError:
|
||||
# logging.info(
|
||||
# f'{ERROR} \"{id}\" was reported as missing, but is not in the file_to_entry_id map.')
|
||||
# yield i
|
||||
# for d in deleted:
|
||||
# self.lib.missing_files.remove(d)
|
||||
# # self.driver.filter_items('')
|
||||
# # self.done.emit()
|
||||
|
||||
|
||||
class RelinkUnlinkedEntries(QObject):
|
||||
done = Signal()
|
||||
def __init__(self, library:'Library', driver:'QtDriver'):
|
||||
super().__init__()
|
||||
self.lib = library
|
||||
self.driver:QtDriver = driver
|
||||
self.fixed = 0
|
||||
|
||||
def repair_entries(self):
|
||||
# pb = QProgressDialog('', None, 0, len(self.lib.missing_files))
|
||||
# # pb.setMaximum(len(self.lib.missing_files))
|
||||
# pb.setFixedSize(432, 112)
|
||||
# pb.setWindowFlags(pb.windowFlags() & ~Qt.WindowType.WindowCloseButtonHint)
|
||||
# pb.setWindowTitle('Relinking Entries')
|
||||
# pb.setWindowModality(Qt.WindowModality.ApplicationModal)
|
||||
# pb.show()
|
||||
|
||||
# r = CustomRunnable(lambda: self.repair_entries_runnable(pb))
|
||||
# r.done.connect(lambda: self.done.emit())
|
||||
# # r.done.connect(lambda: self.model.clear())
|
||||
# QThreadPool.globalInstance().start(r)
|
||||
# # r.run()
|
||||
|
||||
|
||||
|
||||
|
||||
iterator = FunctionIterator(self.lib.fix_missing_files)
|
||||
|
||||
pw = ProgressWidget(
|
||||
window_title='Relinking Entries',
|
||||
label_text='',
|
||||
cancel_button_text=None,
|
||||
minimum=0,
|
||||
maximum=len(self.lib.missing_files)
|
||||
)
|
||||
pw.show()
|
||||
|
||||
iterator.value.connect(lambda x: pw.update_progress(x[0]+1))
|
||||
iterator.value.connect(lambda x: (self.increment_fixed() if x[1] else (), pw.update_label(f'Attempting to Relink {x[0]+1}/{len(self.lib.missing_files)} Entries, {self.fixed} Successfully Relinked')))
|
||||
# iterator.value.connect(lambda x: self.driver.purge_item_from_navigation(ItemType.ENTRY, x[1]))
|
||||
|
||||
r = CustomRunnable(lambda:iterator.run())
|
||||
r.done.connect(lambda: (pw.hide(), pw.deleteLater(), self.done.emit(), self.reset_fixed()))
|
||||
QThreadPool.globalInstance().start(r)
|
||||
|
||||
def increment_fixed(self):
|
||||
self.fixed += 1
|
||||
|
||||
def reset_fixed(self):
|
||||
self.fixed = 0
|
||||
|
||||
# def repair_entries_runnable(self, pb: QProgressDialog):
|
||||
# fixed = 0
|
||||
# for i in self.lib.fix_missing_files():
|
||||
# if i[1]:
|
||||
# fixed += 1
|
||||
# pb.setValue(i[0])
|
||||
# pb.setLabelText(f'Attempting to Relink {i[0]+1}/{len(self.lib.missing_files)} Entries, {fixed} Successfully Relinked')
|
||||
|
||||
# for i, missing in enumerate(self.lib.missing_files):
|
||||
# pb.setValue(i)
|
||||
# pb.setLabelText(f'Relinking {i}/{len(self.lib.missing_files)} Unlinked Entries')
|
||||
# self.lib.fix_missing_files()
|
||||
# try:
|
||||
# id = self.lib.get_entry_id_from_filepath(missing)
|
||||
# logging.info(f'Removing Entry ID {id}:\n\t{missing}')
|
||||
# self.lib.remove_entry(id)
|
||||
# self.driver.purge_item_from_navigation(ItemType.ENTRY, id)
|
||||
# deleted.append(missing)
|
||||
# except KeyError:
|
||||
# logging.info(
|
||||
# f'{ERROR} \"{id}\" was reported as missing, but is not in the file_to_entry_id map.')
|
||||
# for d in deleted:
|
||||
# self.lib.missing_files.remove(d)
|
||||
|
||||
|
||||
class PreviewPanel(QWidget):
|
||||
"""The Preview Panel Widget."""
|
||||
tags_updated = Signal()
|
||||
|
@ -1921,17 +1318,6 @@ class ItemThumb(FlowWidget):
|
|||
# e.remove_tag(1)
|
||||
|
||||
|
||||
class CustomRunnable(QRunnable, QObject):
|
||||
done = Signal()
|
||||
def __init__(self, function) -> None:
|
||||
QRunnable.__init__(self)
|
||||
QObject.__init__(self)
|
||||
self.function = function
|
||||
|
||||
def run(self):
|
||||
self.function()
|
||||
self.done.emit()
|
||||
|
||||
class QtDriver(QObject):
|
||||
"""A Qt GUI frontend driver for TagStudio."""
|
||||
|
||||
|
@ -2974,258 +2360,3 @@ class QtDriver(QObject):
|
|||
end_time = time.time()
|
||||
self.main_window.statusbar.showMessage(f'Collage Saved at "{filename}" ({format_timespan(end_time - self.collage_start_time)})')
|
||||
logging.info(f'Collage Saved at "{filename}" ({format_timespan(end_time - self.collage_start_time)})')
|
||||
|
||||
class FoldersToTagsModal(QWidget):
|
||||
# done = Signal(int)
|
||||
def __init__(self, library:'Library', driver:'QtDriver'):
|
||||
super().__init__()
|
||||
self.library = library
|
||||
self.driver:QtDriver = 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.clicked.connect(lambda: self.folders_to_tags(self.library))
|
||||
|
||||
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_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 = self.TreeItemTest(folder,None)
|
||||
self.scroll_layout.addWidget(test)
|
||||
|
||||
|
||||
def generate_preview_data(self,library:Library):
|
||||
tree = dict(dirs={},files=[])
|
||||
|
||||
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,files=[])
|
||||
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,"",[],[],"green")
|
||||
branch["dirs"][folder] = dict(dirs={},tag=new_tag,files=[])
|
||||
branch = branch["dirs"][folder]
|
||||
return branch
|
||||
|
||||
for tag in library.tags:
|
||||
reversed_tag = self.reverse_tag(tag,None)
|
||||
logging.info(set(map(lambda tag:tag.name ,reversed_tag)))
|
||||
add_tag_to_tree(reversed_tag)
|
||||
|
||||
for entry in library.entries:
|
||||
folders = entry.path.split("\\")
|
||||
if len(folders) == 1 and folders[0] == "": continue
|
||||
branch = add_folders_to_tree(folders)
|
||||
if branch:
|
||||
field_indexes = library.get_field_index_in_entry(entry,6)
|
||||
has_tag=False
|
||||
for index in field_indexes:
|
||||
content = library.get_field_attr(entry.fields[index],"content")
|
||||
for tag_id in content:
|
||||
tag = library.get_tag(tag_id)
|
||||
if tag.name == branch["tag"].name:
|
||||
has_tag=True
|
||||
break
|
||||
if not has_tag:
|
||||
branch["files"].append(entry.filename)
|
||||
|
||||
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
|
||||
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]
|
||||
|
||||
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 = self.reverse_tag(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)
|
||||
|
||||
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.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())
|
||||
|
||||
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()
|
||||
|
|
Loading…
Reference in a new issue