Merge remote-tracking branch 'upstream/main'

This commit is contained in:
DrRetro
2024-04-25 09:46:22 -04:00
15 changed files with 227 additions and 129 deletions

View File

@@ -5,24 +5,22 @@
"""The Library object and related methods for TagStudio."""
import datetime
from enum import Enum
import os
import traceback
from typing import Optional
import json
import glob
from pathlib import Path
# from typing_extensions import deprecated
import src.core.ts_core as ts_core
from src.core.utils.web import *
from src.core.utils.str import *
from src.core.utils.fs import *
import xml.etree.ElementTree as ET
import json
import logging
import os
import sys
import time
import logging
import traceback
import xml.etree.ElementTree as ET
from enum import Enum
import ujson
from src.core import ts_core
from src.core.utils.str import strip_punctuation
from src.core.utils.web import strip_web_protocol
TYPE = ['file', 'meta', 'alt', 'mask']
# RESULT_TYPE = Enum('Result', ['ENTRY', 'COLLATION', 'TAG_GROUP'])
class ItemType(Enum):
@@ -576,7 +574,7 @@ class Library:
if not os.path.isdir(full_collage_path):
os.mkdir(full_collage_path)
def verify_default_tags(self, tag_list: list) -> dict:
def verify_default_tags(self, tag_list: list) -> list:
"""
Ensures that the default builtin tags are present in the Library's
save file. Takes in and returns the tag dictionary from the JSON file.
@@ -630,41 +628,45 @@ class Library:
if 'id' in tag.keys():
id = tag['id']
if int(id) >= self._next_tag_id:
self._next_tag_id = int(id) + 1
# Don't load tags with duplicate IDs
if id not in [t.id for t in self.tags]:
if int(id) >= self._next_tag_id:
self._next_tag_id = int(id) + 1
name = ''
if 'name' in tag.keys():
name = tag['name']
shorthand = ''
if 'shorthand' in tag.keys():
shorthand = tag['shorthand']
aliases = []
if 'aliases' in tag.keys():
aliases = tag['aliases']
subtag_ids = []
if 'subtag_ids' in tag.keys():
subtag_ids = tag['subtag_ids']
color = ''
if 'color' in tag.keys():
color = tag['color']
name = ''
if 'name' in tag.keys():
name = tag['name']
shorthand = ''
if 'shorthand' in tag.keys():
shorthand = tag['shorthand']
aliases = []
if 'aliases' in tag.keys():
aliases = tag['aliases']
subtag_ids = []
if 'subtag_ids' in tag.keys():
subtag_ids = tag['subtag_ids']
color = ''
if 'color' in tag.keys():
color = tag['color']
t = Tag(
id=int(id),
name=name,
shorthand=shorthand,
aliases=aliases,
subtags_ids=subtag_ids,
color=color
)
t = Tag(
id=int(id),
name=name,
shorthand=shorthand,
aliases=aliases,
subtags_ids=subtag_ids,
color=color
)
# NOTE: This does NOT use the add_tag_to_library() method!
# That method is only used for Tags added at runtime.
# This process uses the same inner methods, but waits until all of the
# Tags are registered in the Tags list before creating the Tag clusters.
self.tags.append(t)
self._map_tag_id_to_index(t, -1)
self._map_tag_strings_to_tag_id(t)
# NOTE: This does NOT use the add_tag_to_library() method!
# That method is only used for Tags added at runtime.
# This process uses the same inner methods, but waits until all of the
# Tags are registered in the Tags list before creating the Tag clusters.
self.tags.append(t)
self._map_tag_id_to_index(t, -1)
self._map_tag_strings_to_tag_id(t)
else:
logging.info(f'[LIBRARY]Skipping Tag with duplicate ID: {tag}')
# Step 3: Map each Tag's subtags together now that all Tag objects in it.
for t in self.tags:
@@ -856,10 +858,10 @@ class Library:
}
print('[LIBRARY] Formatting Tags to JSON...')
file_to_save['tags'] = self.verify_default_tags(file_to_save['tags'])
for tag in self.tags:
file_to_save["tags"].append(tag.compressed_dict())
file_to_save['tags'] = self.verify_default_tags(file_to_save['tags'])
print('[LIBRARY] Formatting Entries to JSON...')
for entry in self.entries:
file_to_save["entries"].append(entry.compressed_dict())

View File

@@ -4,16 +4,10 @@
"""The core classes and methods of TagStudio."""
import os
from types import FunctionType
# from typing import Dict, Optional, TypedDict, List
import json
from pathlib import Path
import traceback
# import requests
# from bs4 import BeautifulSoup as bs
from src.core.library import *
from src.core.field_template import FieldTemplate
import os
from src.core.library import Entry, Library
VERSION: str = '9.1.0' # Major.Minor.Patch
VERSION_BRANCH: str = 'Alpha' # 'Alpha', 'Beta', or '' for Full Release
@@ -37,11 +31,11 @@ SPREADSHEET_TYPES: list[str] = ['csv', 'xls', 'xlsx', 'numbers', 'ods']
PRESENTATION_TYPES: list[str] = ['ppt', 'pptx', 'key', 'odp']
ARCHIVE_TYPES: list[str] = ['zip', 'rar', 'tar', 'tar.gz', 'tgz', '7z']
PROGRAM_TYPES: list[str] = ['exe', 'app']
SHORTCUT_TYPES: list[str] = ['lnk', 'desktop']
SHORTCUT_TYPES: list[str] = ['lnk', 'desktop', 'url']
ALL_FILE_TYPES: list[str] = IMAGE_TYPES + VIDEO_TYPES + AUDIO_TYPES + \
TEXT_TYPES + SPREADSHEET_TYPES + PRESENTATION_TYPES + \
ARCHIVE_TYPES + PROGRAM_TYPES
ARCHIVE_TYPES + PROGRAM_TYPES + SHORTCUT_TYPES
BOX_FIELDS = ['tag_box', 'text_box']
TEXT_FIELDS = ['text_line', 'text_box']

View File

@@ -2,9 +2,6 @@
# Licensed under the GPL-3.0 License.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
import os
def clean_folder_name(folder_name: str) -> str:
cleaned_name = folder_name
invalid_chars = "<>:\"/\\|?*."

View File

@@ -4,9 +4,8 @@
"""PySide6 port of the widgets/layouts/flowlayout example from Qt v6.x"""
import sys
from PySide6.QtCore import Qt, QMargins, QPoint, QRect, QSize
from PySide6.QtWidgets import QApplication, QLayout, QPushButton, QSizePolicy, QWidget
from PySide6.QtWidgets import QLayout, QSizePolicy, QWidget
# class Window(QWidget):

View File

@@ -12,23 +12,14 @@
# Licensed under the GPL-3.0 License.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
from re import S
import time
from typing import Optional
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform, QAction)
from PySide6.QtWidgets import (QApplication, QComboBox, QFrame, QGridLayout,
from PySide6.QtCore import (QCoreApplication, QMetaObject, QRect,
QSize, Qt)
from PySide6.QtGui import (QFont, QAction)
from PySide6.QtWidgets import (QComboBox, QFrame, QGridLayout,
QHBoxLayout, QVBoxLayout, QLayout, QLineEdit, QMainWindow,
QMenuBar, QPushButton, QScrollArea, QSizePolicy,
QStatusBar, QWidget, QSplitter, QMenu)
from src.qt.pagination import Pagination
# from src.qt.qtacrylic.qtacrylic import WindowEffect
# from qframelesswindow import FramelessMainWindow, StandardTitleBar
class Ui_MainWindow(QMainWindow):

View File

@@ -5,10 +5,10 @@
"""A pagination widget created for TagStudio."""
# I never want to see this code again.
from PySide6 import QtCore
from PySide6.QtGui import *
from PySide6.QtWidgets import *
from PySide6.QtCore import QFile, QObject, QThread, Signal, QRunnable, Qt, QThreadPool, QSize, QEvent, QMimeData
from PySide6.QtCore import QObject, Signal, QSize
from PySide6.QtGui import QIntValidator
from PySide6.QtWidgets import QWidget, QHBoxLayout, QPushButton, QLabel, QLineEdit, QSizePolicy
# class NumberEdit(QLineEdit):
# def __init__(self, parent=None) -> None:

View File

@@ -7,40 +7,46 @@
"""A Qt driver for TagStudio."""
from copy import copy, deepcopy
import ctypes
import math
from os import times
import sys
import logging
import threading
from time import sleep
from queue import Empty, Queue
import math
import os
import sys
import time
from typing import Optional, Union
from PySide6 import QtCore
import PySide6
from PySide6.QtGui import *
from PySide6.QtWidgets import *
from PySide6.QtCore import QFile, QObject, QThread, Signal, QRunnable, Qt, QThreadPool, QSize, QEvent, QMimeData, QTimer
from PySide6.QtUiTools import QUiLoader
from PIL import Image, ImageOps, ImageChops, UnidentifiedImageError, ImageQt, ImageDraw, ImageFont, ImageEnhance
import PySide6.QtWidgets
import humanfriendly
import pillow_avif
import cv2
import traceback
import shutil
import subprocess
from types import FunctionType
from datetime import datetime as dt
from src.core.ts_core import *
# from src.core.utils.web import *
# from src.core.utils.fs import *
from src.core.library import *
from pathlib import Path
from queue import Empty, Queue
from time import sleep
from typing import Optional
import cv2
from PIL import Image, ImageChops, UnidentifiedImageError, ImageQt, ImageDraw, ImageFont, ImageEnhance
from PySide6 import QtCore
from PySide6.QtCore import QObject, QThread, Signal, QRunnable, Qt, QThreadPool, QSize, QEvent, QTimer
from PySide6.QtGui import (QGuiApplication, QPixmap, QEnterEvent, QMouseEvent, QResizeEvent, QPainter, QColor, QPen,
QAction, QStandardItemModel, QStandardItem, QPainterPath, QFontDatabase, QIcon)
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QPlainTextEdit,
QLineEdit, QScrollArea, QFrame, QTextEdit, QComboBox, QProgressDialog, QFileDialog,
QListView, 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.ts_core import (TagStudioCore, TAG_COLORS, DATE_FIELDS, TEXT_FIELDS, BOX_FIELDS, ALL_FILE_TYPES,
SHORTCUT_TYPES, PROGRAM_TYPES, ARCHIVE_TYPES, PRESENTATION_TYPES,
SPREADSHEET_TYPES, TEXT_TYPES, AUDIO_TYPES, VIDEO_TYPES, IMAGE_TYPES,
LIBRARY_FILENAME, COLLAGE_FOLDER_NAME, BACKUP_FOLDER_NAME, TS_FOLDER_NAME,
VERSION_BRANCH, VERSION)
from src.core.utils.web import strip_web_protocol
from src.qt.flowlayout import FlowLayout, FlowWidget
from src.qt.main_window import Ui_MainWindow
import src.qt.resources_rc
# from typing_extensions import deprecated
from humanfriendly import format_timespan
# from src.qt.qtacrylic.qtacrylic import WindowEffect
# SIGQUIT is not defined on Windows
if sys.platform == "win32":
@@ -56,11 +62,20 @@ INFO = f'[INFO]'
logging.basicConfig(format="%(message)s", level=logging.INFO)
def open_file(path):
def open_file(path: str):
try:
os.startfile(path)
except FileNotFoundError:
logging.info('File Not Found! (Imagine this as a popup)')
if sys.platform == "win32":
subprocess.Popen(["start", path], shell=True, close_fds=True, creationflags=subprocess.DETACHED_PROCESS)
else:
if sys.platform == "darwin":
command_name = "open"
else:
command_name = "xdg-open"
command = shutil.which(command_name)
if command is not None:
subprocess.Popen([command, path], close_fds=True)
else:
logging.info(f"Could not find {command_name} on system PATH")
except:
traceback.print_exc()
@@ -2131,12 +2146,12 @@ class PreviewPanel(QWidget):
# Stats for specific file types are displayed here.
if extension in (IMAGE_TYPES + VIDEO_TYPES):
self.dimensions_label.setText(f"{extension.upper()}{humanfriendly.format_size(os.stat(filepath).st_size)}\n{image.width} x {image.height} px")
self.dimensions_label.setText(f"{extension.upper()}{format_size(os.stat(filepath).st_size)}\n{image.width} x {image.height} px")
else:
self.dimensions_label.setText(f"{extension.upper()}")
if not image:
self.dimensions_label.setText(f"{extension.upper()}{humanfriendly.format_size(os.stat(filepath).st_size)}")
self.dimensions_label.setText(f"{extension.upper()}{format_size(os.stat(filepath).st_size)}")
raise UnidentifiedImageError
except (UnidentifiedImageError, FileNotFoundError, cv2.error):
@@ -4534,7 +4549,7 @@ class QtDriver(QObject):
self.completed += 1
# logging.info(f'threshold:{len(self.lib.entries}, completed:{self.completed}')
if self.completed == len(self.lib.entries):
filename = os.path.normpath(f'{self.lib.library_dir}/{TS_FOLDER_NAME}/{COLLAGE_FOLDER_NAME}/collage_{datetime.datetime.utcnow().strftime("%F_%T").replace(":", "")}.png')
filename = os.path.normpath(f'{self.lib.library_dir}/{TS_FOLDER_NAME}/{COLLAGE_FOLDER_NAME}/collage_{dt.utcnow().strftime("%F_%T").replace(":", "")}.png')
self.collage.save(filename)
self.collage = None

View File

@@ -9,7 +9,6 @@ from src.cli.ts_cli import CliDriver
from src.qt.ts_qt import QtDriver
import argparse
import traceback
# import ctypes
def main():