This repository has been archived on 2023-07-12. You can view files and clone it, but cannot push or open issues or pull requests.
athnos/athnos/window/window.py
2022-04-28 19:27:52 +02:00

1067 lines
55 KiB
Python
Executable File

from PyQt5 import QtWidgets, QtSql, QtCore, QtGui
from ..database import Clip, Database, Type
from pathlib import Path
from zipfile import ZipFile, ZIP_DEFLATED
import shutil
import os
from .. import utils as utils
from .. import config, logger
from .source import EditSource, ShowSource
from .add import AddClip, AddSource
from .connect import Connect
from . import _Window
import json
from .QCustomObject import QTagEdit, QTableSortFilterProxyModel
import re
from . import resources
# the database, global available
_database = None
class Window(_Window):
"""The main window class"""
default_driver = "QSQLITE"
default_name = str(Path().absolute().joinpath('athnos.sqlite3'))
default_user = "root"
default_password = ""
default_ip = "127.0.0.1"
default_port = 3306
def __init__(self, application: QtWidgets.QApplication, main_window: QtWidgets.QMainWindow = None,
driver=default_driver, name=default_name, user=default_user, password=default_password,
ip=default_ip, port=default_port):
"""
Setup the main window and database connection
Args:
main_window: main window
driver: The sql driver. See `connect.Connect.drivers` for all supported drivers
name: Name of the database, or path to the database if it's stored in a file
user: SQL username
password: SQL password
ip: IP of the database
port: Port of the database
"""
global _database
self.application = application
if not main_window:
main_window = QtWidgets.QMainWindow()
# setup all internal variables
self._id = 0
self._clip: Clip = None
self._row_number = -1
self._all_ids = []
self._columns = {}
# passing all given arguments to class wide arguments
self.db = QtSql.QSqlDatabase(driver)
if name:
self.db.setDatabaseName(name)
if user:
self.db.setUserName(user)
if password:
self.db.setPassword(password)
if ip:
self.db.setHostName(ip)
if port:
self.db.setPort(port)
# tries to open a database connection
if not self.db.open():
raise IOError(self.db.lastError().text())
# setup all tables
_database = Database(self.db)
self.database = _database
# the default search filter rules (Tag, Name, Description, Type)
self.filter_rules = [True, True, False, True]
# all filter items
self.filter_items_lower = ('tag', 'name', 'description', 'type')
# the model where all the data will be stored in
self.model: QTableSortFilterProxyModel = None
super().__init__(main_window)
# on startup
self.load_model()
self.on_filter_box_change(None)
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(923, 684)
MainWindow.setTabShape(QtWidgets.QTabWidget.Rounded)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
self.verticalLayout.setObjectName("verticalLayout")
self.main_splitter = QtWidgets.QSplitter(self.centralwidget)
self.main_splitter.setEnabled(True)
self.main_splitter.setOrientation(QtCore.Qt.Horizontal)
self.main_splitter.setOpaqueResize(True)
self.main_splitter.setObjectName("main_splitter")
self.verticalLayoutWidget = QtWidgets.QWidget(self.main_splitter)
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
self.search_vlayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
self.search_vlayout.setContentsMargins(0, 0, 0, 0)
self.search_vlayout.setObjectName("search_vlayout")
self.clip_hlayout = QtWidgets.QGroupBox(self.verticalLayoutWidget)
self.clip_hlayout.setStyleSheet("QGroupBox {border: 1px solid grey; margin-top: 0.5em; font: 12px consolas;} QGroupBox::title {top: -6px; left: 10px}")
self.clip_hlayout.setObjectName("clip_hlayout")
self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.clip_hlayout)
self.verticalLayout_6.setObjectName("verticalLayout_6")
spacerItem = QtWidgets.QSpacerItem(20, 5, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
self.verticalLayout_6.addItem(spacerItem)
self.search_box_hlayout = QtWidgets.QHBoxLayout()
self.search_box_hlayout.setObjectName("search_box_hlayout")
self.search_input = QtWidgets.QLineEdit(self.clip_hlayout)
self.search_input.setPlaceholderText("")
self.search_input.setClearButtonEnabled(True)
self.search_input.setObjectName("search_input")
self.search_box_hlayout.addWidget(self.search_input)
self.filter_box = QtWidgets.QComboBox(self.clip_hlayout)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.filter_box.sizePolicy().hasHeightForWidth())
self.filter_box.setSizePolicy(sizePolicy)
self.filter_box.setCurrentText("")
self.filter_box.setObjectName("filter_box")
self.search_box_hlayout.addWidget(self.filter_box)
self.verticalLayout_6.addLayout(self.search_box_hlayout)
self.clip_view = QtWidgets.QTableView(self.clip_hlayout)
self.clip_view.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
self.clip_view.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.clip_view.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
self.clip_view.setAutoScroll(True)
self.clip_view.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.clip_view.setDragEnabled(False)
self.clip_view.setAlternatingRowColors(True)
self.clip_view.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
self.clip_view.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.clip_view.setSortingEnabled(True)
self.clip_view.setObjectName("clip_view")
self.clip_view.horizontalHeader().setSortIndicatorShown(True)
self.verticalLayout_6.addWidget(self.clip_view)
self.search_vlayout.addWidget(self.clip_hlayout)
self.clip_source_splitter = QtWidgets.QSplitter(self.main_splitter)
self.clip_source_splitter.setEnabled(True)
self.clip_source_splitter.setOrientation(QtCore.Qt.Vertical)
self.clip_source_splitter.setObjectName("clip_source_splitter")
self.clip_group_box = QtWidgets.QGroupBox(self.clip_source_splitter)
self.clip_group_box.setEnabled(True)
self.clip_group_box.setStyleSheet("QGroupBox {border: 1px solid grey; margin-top: 0.5em; font: 12px consolas;} QGroupBox::title {top: -6px; left: 10px}")
self.clip_group_box.setObjectName("clip_group_box")
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.clip_group_box)
self.verticalLayout_3.setObjectName("verticalLayout_3")
spacerItem1 = QtWidgets.QSpacerItem(20, 5, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
self.verticalLayout_3.addItem(spacerItem1)
self.file_url_hlayout = QtWidgets.QHBoxLayout()
self.file_url_hlayout.setObjectName("file_url_hlayout")
self.file_url_label = QtWidgets.QLabel(self.clip_group_box)
self.file_url_label.setMinimumSize(QtCore.QSize(70, 0))
self.file_url_label.setObjectName("file_url_label")
self.file_url_hlayout.addWidget(self.file_url_label)
self.file_url_edit = QtWidgets.QLineEdit(self.clip_group_box)
self.file_url_edit.setObjectName("file_url_edit")
self.file_url_hlayout.addWidget(self.file_url_edit)
self.verticalLayout_3.addLayout(self.file_url_hlayout)
self.extended_view_clip_line = QtWidgets.QFrame(self.clip_group_box)
self.extended_view_clip_line.setFrameShape(QtWidgets.QFrame.HLine)
self.extended_view_clip_line.setFrameShadow(QtWidgets.QFrame.Sunken)
self.extended_view_clip_line.setObjectName("extended_view_clip_line")
self.verticalLayout_3.addWidget(self.extended_view_clip_line)
self.name_hlayout = QtWidgets.QHBoxLayout()
self.name_hlayout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
self.name_hlayout.setObjectName("name_hlayout")
self.name_label = QtWidgets.QLabel(self.clip_group_box)
self.name_label.setEnabled(True)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.name_label.sizePolicy().hasHeightForWidth())
self.name_label.setSizePolicy(sizePolicy)
self.name_label.setMinimumSize(QtCore.QSize(70, 0))
self.name_label.setObjectName("name_label")
self.name_hlayout.addWidget(self.name_label)
self.name_edit = QtWidgets.QLineEdit(self.clip_group_box)
self.name_edit.setEnabled(True)
self.name_edit.setReadOnly(False)
self.name_edit.setObjectName("name_edit")
self.name_hlayout.addWidget(self.name_edit)
self.verticalLayout_3.addLayout(self.name_hlayout)
self.description_hlayout = QtWidgets.QHBoxLayout()
self.description_hlayout.setObjectName("description_hlayout")
self.description_label = QtWidgets.QLabel(self.clip_group_box)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.description_label.sizePolicy().hasHeightForWidth())
self.description_label.setSizePolicy(sizePolicy)
self.description_label.setMinimumSize(QtCore.QSize(70, 0))
self.description_label.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
self.description_label.setWordWrap(False)
self.description_label.setOpenExternalLinks(False)
self.description_label.setObjectName("description_label")
self.description_hlayout.addWidget(self.description_label)
self.description_edit = QtWidgets.QPlainTextEdit(self.clip_group_box)
self.description_edit.setEnabled(True)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.description_edit.sizePolicy().hasHeightForWidth())
self.description_edit.setSizePolicy(sizePolicy)
self.description_edit.setReadOnly(False)
self.description_edit.setObjectName("description_edit")
self.description_hlayout.addWidget(self.description_edit)
self.verticalLayout_3.addLayout(self.description_hlayout)
self.line = QtWidgets.QFrame(self.clip_group_box)
self.line.setFrameShape(QtWidgets.QFrame.HLine)
self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
self.line.setObjectName("line")
self.verticalLayout_3.addWidget(self.line)
self.type_hlayout = QtWidgets.QHBoxLayout()
self.type_hlayout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
self.type_hlayout.setObjectName("type_hlayout")
self.type_label = QtWidgets.QLabel(self.clip_group_box)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.type_label.sizePolicy().hasHeightForWidth())
self.type_label.setSizePolicy(sizePolicy)
self.type_label.setMinimumSize(QtCore.QSize(70, 0))
self.type_label.setObjectName("type_label")
self.type_hlayout.addWidget(self.type_label)
self.type_box = QtWidgets.QComboBox(self.clip_group_box)
self.type_box.setEnabled(True)
self.type_box.setEditable(False)
self.type_box.setDuplicatesEnabled(False)
self.type_box.setProperty("placeholderText", "")
self.type_box.setObjectName("type_box")
self.type_box.addItem("")
self.type_box.addItem("")
self.type_box.addItem("")
self.type_box.addItem("")
self.type_hlayout.addWidget(self.type_box)
self.verticalLayout_3.addLayout(self.type_hlayout)
self.tags_hlayout = QtWidgets.QHBoxLayout()
self.tags_hlayout.setObjectName("tags_hlayout")
self.tags_label = QtWidgets.QLabel(self.clip_group_box)
self.tags_label.setMinimumSize(QtCore.QSize(70, 0))
self.tags_label.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
self.tags_label.setObjectName("tags_label")
self.tags_hlayout.addWidget(self.tags_label)
self.tags_edit = QtWidgets.QScrollArea(self.clip_group_box)
self.tags_edit.setEnabled(True)
self.tags_edit.setWidgetResizable(True)
self.tags_edit.setObjectName("tags_edit")
self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 483, 83))
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.tags_edit.setWidget(self.scrollAreaWidgetContents)
self.tags_hlayout.addWidget(self.tags_edit)
self.verticalLayout_3.addLayout(self.tags_hlayout)
self.editing_hlayout = QtWidgets.QHBoxLayout()
self.editing_hlayout.setObjectName("editing_hlayout")
self.save_button = QtWidgets.QPushButton(self.clip_group_box)
self.save_button.setEnabled(True)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.save_button.sizePolicy().hasHeightForWidth())
self.save_button.setSizePolicy(sizePolicy)
self.save_button.setObjectName("save_button")
self.editing_hlayout.addWidget(self.save_button)
spacerItem2 = QtWidgets.QSpacerItem(15, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum)
self.editing_hlayout.addItem(spacerItem2)
self.enable_editing_check_box = QtWidgets.QCheckBox(self.clip_group_box)
self.enable_editing_check_box.setEnabled(True)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.enable_editing_check_box.sizePolicy().hasHeightForWidth())
self.enable_editing_check_box.setSizePolicy(sizePolicy)
self.enable_editing_check_box.setObjectName("enable_editing_check_box")
self.editing_hlayout.addWidget(self.enable_editing_check_box)
spacerItem3 = QtWidgets.QSpacerItem(15, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum)
self.editing_hlayout.addItem(spacerItem3)
self.delete_button = QtWidgets.QPushButton(self.clip_group_box)
self.delete_button.setEnabled(True)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.delete_button.sizePolicy().hasHeightForWidth())
self.delete_button.setSizePolicy(sizePolicy)
self.delete_button.setCheckable(False)
self.delete_button.setChecked(False)
self.delete_button.setObjectName("delete_button")
self.editing_hlayout.addWidget(self.delete_button)
self.verticalLayout_3.addLayout(self.editing_hlayout)
self.source_group_box = QtWidgets.QGroupBox(self.clip_source_splitter)
self.source_group_box.setStyleSheet("QGroupBox {border: 1px solid grey; margin-top: 0.5em; font: 12px consolas;} QGroupBox::title {top: -6px; left: 10px}")
self.source_group_box.setObjectName("source_group_box")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.source_group_box)
self.verticalLayout_2.setObjectName("verticalLayout_2")
spacerItem4 = QtWidgets.QSpacerItem(20, 5, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
self.verticalLayout_2.addItem(spacerItem4)
self.from_hlayout = QtWidgets.QHBoxLayout()
self.from_hlayout.setObjectName("from_hlayout")
self.from_label = QtWidgets.QLabel(self.source_group_box)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.from_label.sizePolicy().hasHeightForWidth())
self.from_label.setSizePolicy(sizePolicy)
self.from_label.setObjectName("from_label")
self.from_hlayout.addWidget(self.from_label)
self.source_name = QtWidgets.QLineEdit(self.source_group_box)
self.source_name.setEnabled(True)
self.source_name.setText("")
self.source_name.setReadOnly(False)
self.source_name.setObjectName("source_name")
self.from_hlayout.addWidget(self.source_name)
spacerItem5 = QtWidgets.QSpacerItem(7, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum)
self.from_hlayout.addItem(spacerItem5)
self.season_label = QtWidgets.QLabel(self.source_group_box)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.season_label.sizePolicy().hasHeightForWidth())
self.season_label.setSizePolicy(sizePolicy)
self.season_label.setObjectName("season_label")
self.from_hlayout.addWidget(self.season_label)
self.source_season = QtWidgets.QLineEdit(self.source_group_box)
self.source_season.setEnabled(True)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.source_season.sizePolicy().hasHeightForWidth())
self.source_season.setSizePolicy(sizePolicy)
self.source_season.setMaximumSize(QtCore.QSize(50, 16777215))
self.source_season.setBaseSize(QtCore.QSize(0, 0))
self.source_season.setReadOnly(False)
self.source_season.setObjectName("source_season")
self.from_hlayout.addWidget(self.source_season)
spacerItem6 = QtWidgets.QSpacerItem(7, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum)
self.from_hlayout.addItem(spacerItem6)
self.episode_label = QtWidgets.QLabel(self.source_group_box)
self.episode_label.setObjectName("episode_label")
self.from_hlayout.addWidget(self.episode_label)
self.source_episode = QtWidgets.QLineEdit(self.source_group_box)
self.source_episode.setEnabled(True)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.source_episode.sizePolicy().hasHeightForWidth())
self.source_episode.setSizePolicy(sizePolicy)
self.source_episode.setMaximumSize(QtCore.QSize(50, 16777215))
self.source_episode.setBaseSize(QtCore.QSize(0, 0))
self.source_episode.setReadOnly(False)
self.source_episode.setObjectName("source_episode")
self.from_hlayout.addWidget(self.source_episode)
self.verticalLayout_2.addLayout(self.from_hlayout)
self.source_descriptio_hlayout = QtWidgets.QHBoxLayout()
self.source_descriptio_hlayout.setObjectName("source_descriptio_hlayout")
self.source_description_label = QtWidgets.QLabel(self.source_group_box)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.source_description_label.sizePolicy().hasHeightForWidth())
self.source_description_label.setSizePolicy(sizePolicy)
self.source_description_label.setMinimumSize(QtCore.QSize(70, 0))
self.source_description_label.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
self.source_description_label.setObjectName("source_description_label")
self.source_descriptio_hlayout.addWidget(self.source_description_label)
self.source_description_edit = QtWidgets.QPlainTextEdit(self.source_group_box)
self.source_description_edit.setEnabled(True)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.source_description_edit.sizePolicy().hasHeightForWidth())
self.source_description_edit.setSizePolicy(sizePolicy)
self.source_description_edit.setObjectName("source_description_edit")
self.source_descriptio_hlayout.addWidget(self.source_description_edit)
self.verticalLayout_2.addLayout(self.source_descriptio_hlayout)
self.source_type_hlayout = QtWidgets.QHBoxLayout()
self.source_type_hlayout.setObjectName("source_type_hlayout")
self.source_type_label = QtWidgets.QLabel(self.source_group_box)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.source_type_label.sizePolicy().hasHeightForWidth())
self.source_type_label.setSizePolicy(sizePolicy)
self.source_type_label.setMinimumSize(QtCore.QSize(70, 0))
self.source_type_label.setObjectName("source_type_label")
self.source_type_hlayout.addWidget(self.source_type_label)
self.source_type_box = QtWidgets.QComboBox(self.source_group_box)
self.source_type_box.setObjectName("source_type_box")
self.source_type_box.addItem("")
self.source_type_box.addItem("")
self.source_type_box.addItem("")
self.source_type_box.addItem("")
self.source_type_hlayout.addWidget(self.source_type_box)
self.verticalLayout_2.addLayout(self.source_type_hlayout)
self.edit_change_hlayout = QtWidgets.QHBoxLayout()
self.edit_change_hlayout.setObjectName("edit_change_hlayout")
self.edit_source_button = QtWidgets.QPushButton(self.source_group_box)
self.edit_source_button.setEnabled(True)
self.edit_source_button.setObjectName("edit_source_button")
self.edit_change_hlayout.addWidget(self.edit_source_button)
self.change_source_button = QtWidgets.QPushButton(self.source_group_box)
self.change_source_button.setEnabled(True)
self.change_source_button.setObjectName("change_source_button")
self.edit_change_hlayout.addWidget(self.change_source_button)
self.verticalLayout_2.addLayout(self.edit_change_hlayout)
self.verticalLayout.addWidget(self.main_splitter)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 923, 30))
self.menubar.setObjectName("menubar")
self.settings_menu = QtWidgets.QMenu(self.menubar)
self.settings_menu.setObjectName("settings_menu")
self.style_menu = QtWidgets.QMenu(self.settings_menu)
self.style_menu.setObjectName("style_menu")
self.update_menu = QtWidgets.QMenu(self.settings_menu)
self.update_menu.setObjectName("update_menu")
self.update_message_menu = QtWidgets.QMenu(self.update_menu)
self.update_message_menu.setObjectName("update_message_menu")
self.menuHelp = QtWidgets.QMenu(self.menubar)
self.menuHelp.setObjectName("menuHelp")
self.file_menu = QtWidgets.QMenu(self.menubar)
self.file_menu.setObjectName("file_menu")
MainWindow.setMenuBar(self.menubar)
self.connect_to_database = QtWidgets.QAction(MainWindow)
self.connect_to_database.setObjectName("connect_to_database")
self.check_for_updates_on_startup = QtWidgets.QAction(MainWindow)
self.check_for_updates_on_startup.setCheckable(True)
self.check_for_updates_on_startup.setChecked(True)
self.check_for_updates_on_startup.setObjectName("check_for_updates_on_startup")
self.check_for_updates = QtWidgets.QAction(MainWindow)
self.check_for_updates.setObjectName("check_for_updates")
self.on_major_update = QtWidgets.QAction(MainWindow)
self.on_major_update.setCheckable(True)
self.on_major_update.setObjectName("on_major_update")
self.on_minor_update = QtWidgets.QAction(MainWindow)
self.on_minor_update.setCheckable(True)
self.on_minor_update.setChecked(True)
self.on_minor_update.setObjectName("on_minor_update")
self.on_patch = QtWidgets.QAction(MainWindow)
self.on_patch.setCheckable(True)
self.on_patch.setObjectName("on_patch")
self.about = QtWidgets.QAction(MainWindow)
self.about.setObjectName("about")
self.file_import = QtWidgets.QAction(MainWindow)
self.file_import.setObjectName("file_import")
self.file_export = QtWidgets.QAction(MainWindow)
self.file_export.setObjectName("file_export")
self.close_window = QtWidgets.QAction(MainWindow)
self.close_window.setObjectName("close_window")
self.add_clip = QtWidgets.QAction(MainWindow)
self.add_clip.setObjectName("add_clip")
self.add_source = QtWidgets.QAction(MainWindow)
self.add_source.setObjectName("add_source")
self.update_message_menu.addAction(self.on_major_update)
self.update_message_menu.addAction(self.on_minor_update)
self.update_message_menu.addAction(self.on_patch)
self.update_menu.addAction(self.check_for_updates_on_startup)
self.update_menu.addSeparator()
self.update_menu.addAction(self.update_message_menu.menuAction())
self.update_menu.addAction(self.check_for_updates)
self.settings_menu.addAction(self.connect_to_database)
self.settings_menu.addSeparator()
self.settings_menu.addAction(self.style_menu.menuAction())
self.settings_menu.addAction(self.update_menu.menuAction())
self.menuHelp.addAction(self.about)
self.file_menu.addAction(self.file_import)
self.file_menu.addAction(self.file_export)
self.file_menu.addSeparator()
self.file_menu.addAction(self.add_clip)
self.file_menu.addAction(self.add_source)
self.file_menu.addSeparator()
self.file_menu.addAction(self.close_window)
self.menubar.addAction(self.file_menu.menuAction())
self.menubar.addAction(self.settings_menu.menuAction())
self.menubar.addAction(self.menuHelp.menuAction())
self.retranslateUi(MainWindow)
self.type_box.setCurrentIndex(3)
self.source_type_box.setCurrentIndex(3)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Athnos"))
self.clip_hlayout.setTitle(_translate("MainWindow", "Search"))
self.clip_group_box.setTitle(_translate("MainWindow", "Clip"))
self.file_url_label.setText(_translate("MainWindow", "File / URL"))
self.name_label.setText(_translate("MainWindow", "Name"))
self.description_label.setText(_translate("MainWindow", "Description"))
self.type_label.setText(_translate("MainWindow", "Type"))
self.type_box.setCurrentText(_translate("MainWindow", "Other"))
self.type_box.setItemText(0, _translate("MainWindow", "Audio"))
self.type_box.setItemText(1, _translate("MainWindow", "Image"))
self.type_box.setItemText(2, _translate("MainWindow", "Video"))
self.type_box.setItemText(3, _translate("MainWindow", "Other"))
self.tags_label.setText(_translate("MainWindow", "Tags"))
self.save_button.setText(_translate("MainWindow", "Save"))
self.enable_editing_check_box.setText(_translate("MainWindow", "Enable editing"))
self.delete_button.setText(_translate("MainWindow", "Delete"))
self.source_group_box.setTitle(_translate("MainWindow", "Source"))
self.from_label.setText(_translate("MainWindow", "From"))
self.season_label.setText(_translate("MainWindow", "Season"))
self.episode_label.setText(_translate("MainWindow", "Episode"))
self.source_description_label.setText(_translate("MainWindow", "Description"))
self.source_type_label.setText(_translate("MainWindow", "Type"))
self.source_type_box.setItemText(0, _translate("MainWindow", "Audio"))
self.source_type_box.setItemText(1, _translate("MainWindow", "Movie"))
self.source_type_box.setItemText(2, _translate("MainWindow", "Series"))
self.source_type_box.setItemText(3, _translate("MainWindow", "Other"))
self.edit_source_button.setText(_translate("MainWindow", "Edit source"))
self.change_source_button.setText(_translate("MainWindow", "Change source"))
self.settings_menu.setTitle(_translate("MainWindow", "Settings"))
self.style_menu.setTitle(_translate("MainWindow", "Style"))
self.update_menu.setTitle(_translate("MainWindow", "Updates"))
self.update_message_menu.setTitle(_translate("MainWindow", "Update message"))
self.menuHelp.setTitle(_translate("MainWindow", "Help"))
self.file_menu.setTitle(_translate("MainWindow", "File"))
self.connect_to_database.setText(_translate("MainWindow", "Connect to database..."))
self.check_for_updates_on_startup.setText(_translate("MainWindow", "Check for updates on startup"))
self.check_for_updates.setText(_translate("MainWindow", "Check for updates..."))
self.on_major_update.setText(_translate("MainWindow", "On major update"))
self.on_minor_update.setText(_translate("MainWindow", "On minor update"))
self.on_patch.setText(_translate("MainWindow", "On patch"))
self.about.setText(_translate("MainWindow", "About"))
self.file_import.setText(_translate("MainWindow", "Import..."))
self.file_export.setText(_translate("MainWindow", "Export..."))
self.close_window.setText(_translate("MainWindow", "Close"))
self.add_clip.setText(_translate("MainWindow", "Add clip..."))
self.add_source.setText(_translate("MainWindow", "Add source..."))
def after_setup_ui(self):
# --- add menu ---
# » add clip
self.add_clip.triggered.connect(lambda: AddClip(QtWidgets.QDialog(self.window, QtCore.Qt.WindowSystemMenuHint)).show())
# » add source
self.add_source.triggered.connect(lambda: AddSource(QtWidgets.QDialog(self.window, QtCore.Qt.WindowSystemMenuHint)).show())
# » import
from .importexport import Import
self.file_import.triggered.connect(lambda: Import(self))
# » close
self.close_window.triggered.connect(self.close)
# --- settings menu ---
# » connect to database
self.connect_to_database.triggered.connect(lambda: self.set_database(Connect(QtWidgets.QDialog(self.window, QtCore.Qt.WindowSystemMenuHint)).show()))
# » set style
for i, style in enumerate(QtWidgets.QStyleFactory.keys()):
style_item = QtWidgets.QAction(style, self.window)
style_item.setCheckable(True)
if style.lower() == QtWidgets.QApplication.style().objectName().lower():
style_item.setChecked(True)
style_item.triggered.connect(lambda checked, style=style_item: self.on_new_style(checked, style))
self.style_menu.addAction(style_item)
# --- search input ---
# » connects `self.on_search_input_change` if the text changes
self.search_input.textChanged.connect(self.on_search_input_change)
# --- filter box ---
# » create the filter box model
self.filter_box_model = QtGui.QStandardItemModel()
# » loop through the `filter_box_items` and add them to the model
for i, item_name in enumerate(self.filter_items_lower):
item_name = item_name[0].upper() + item_name[1:]
item = QtGui.QStandardItem(item_name)
# »» make the item checkable
item.setFlags(QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled)
item.setData(QtCore.Qt.Unchecked, QtCore.Qt.CheckStateRole)
if self.filter_rules[i]:
item.setCheckState(QtCore.Qt.Checked)
else:
item.setCheckState(QtCore.Qt.Unchecked)
# »» add the item to the model
self.filter_box_model.setItem(i, 0, item)
# » apply the filter box model to the box and do some style stuff
self.filter_box.setModel(self.filter_box_model)
self.filter_box.setStyleSheet('QAbstractItemView {min-width: 120px;}')
# » everytime a item is selected or unselected `self.on_filter_box_change` gets called
self.filter_box_model.dataChanged.connect(self.on_filter_box_change)
# » set the line edit (text) of the filter box.
# » normally it's the current selected item text, but i want it to show 'Filter...'
filter_box_edit = QtWidgets.QLineEdit('Filter...')
filter_box_edit.setReadOnly(True)
# » if the 'Apply changes' button is pressed, the filter box text will be still 'Filter...'
filter_box_edit.textChanged.connect(lambda a0: filter_box_edit.setText('Filter...'))
# » if the text edit and not the dropdown arrow is pressed, the dropdown menu will get show anyway
filter_box_edit.mouseReleaseEvent = lambda a0: self.filter_box.showPopup()
self.filter_box.setLineEdit(filter_box_edit)
# --- item / clip view ---
self.clip_view.clicked.connect(self.on_item_clicked)
# --- clip / source ---
action = QtWidgets.QAction()
action.eventFilter = self.open_file_or_link_event_filter
action.setIcon(QtGui.QIcon(':/file_url/internet'))
self.file_url_edit.addAction(action, QtWidgets.QLineEdit.TrailingPosition)
self.enable_editing_check_box.clicked.connect(lambda value: self.set_extended_clip_view(value))
self.save_button.clicked.connect(self.on_save_edited_clip)
self.type_box.setCurrentIndex(-1)
# deletes the old tags edit scroll area
self.tags_edit.deleteLater()
# overwrites `self.tags_edit` and use a `QCustomObject.QTagEdit` instead
self.tags_edit = QTagEdit()
# configures the tage edit
self.tags_edit.setEnabled(False)
self.tags_edit.enableTagSuggestions(True)
self.tags_hlayout.addWidget(self.tags_edit)
self.edit_source_button.clicked.connect(self.on_edit_source)
self.change_source_button.clicked.connect(self.on_change_source)
# search input
self.search_input.textChanged.connect(self.on_search_input_change)
# before startup
self.set_extended_clip_view()
self.set_extended_source_view()
self.clip_source_splitter.setEnabled(False)
def load_model(self) -> None:
"""Creates / loads the `self.model` for the clip view"""
# all currently available clips, tags, clip names, clip descriptions and clip types
self.all_clips = self.database.Clip.get_all()
self.all_tag_names = [tag.name for tag in self.database.Tag.get_all()]
self.all_clip_names = []
self.all_description_texts = []
for clip in self.all_clips:
self.all_clip_names.append(clip.name)
self.all_description_texts.append(clip.description)
self.all_type_names = [type.name for type in Type.Clip]
self.all_names = [self.all_tag_names, self.all_clip_names, self.all_description_texts, self.all_type_names]
self.tags_edit.setTagSuggestions(self.all_tag_names)
# creates the sql model and configures it
sql_model = QtSql.QSqlTableModel()
sql_model.setQuery(self.all_clips.query)
# sets the models query
sql_model.setEditStrategy(QtSql.QSqlTableModel.OnManualSubmit)
# creates a `QCustomObject.QTableSortFilterProxyModel` out of the `sql_model` to filter via the `self.line_input`
model = QTableSortFilterProxyModel(True)
model.setSourceModel(sql_model)
# sets the clip view model and configures the headers
self.clip_view.setModel(model)
self.clip_view.verticalHeader().setHidden(True)
self.clip_view.horizontalHeader().resizeSections(QtWidgets.QHeaderView.Stretch)
self.clip_view.horizontalHeader().setStretchLastSection(True)
type_column = -1
# this sets the header (first letter uppercase, remove id, etc.)
for i in range(model.columnCount()):
header = str(model.headerData(i, QtCore.Qt.Horizontal)).lower()
self._columns[header] = i
# hides the column if its in source_id, id or path
if header in ['source_id', 'id', 'path']:
if header == 'id':
for row in range(model.rowCount()):
self._all_ids.append(int(model.index(row, i).data()))
self.clip_view.hideColumn(i)
continue
elif header == 'type':
type_column = i
# makes the first letter of the header text uppercase
model.setHeaderData(i, QtCore.Qt.Horizontal, header[0].upper() + header[1:])
# creates a new column for the tags
tags_column = model.columnCount()
model.insertColumn(tags_column)
model.setHeaderData(tags_column, QtCore.Qt.Horizontal, 'tags')
# hides the column
self.clip_view.hideColumn(tags_column)
for row in range(model.rowCount()):
# convert the type numbers (type is stored as a number in the sql table) to text
type_index = model.index(row, type_column)
type_name = Type.Clip(model.data(type_index)).name.lower()
model.setData(type_index, type_name[0].upper() + type_name[1:])
# adds the corresponding tags to the index
tags_index = model.index(row, tags_column)
model.setData(tags_index, ', '.join(tag.name for tag in self.all_clips[row].get_tags()))
# makes the model available for the entire class
self.model = model
# self.clip_view.setShowGrid(False)
def set_database(self, database: Database) -> None:
"""Sets the local and global database"""
if database is not None:
# global database
_database = database
# local database
self.database = database
self.load_model()
# --- add menu ---
def on_zip_add(self) -> None:
file = QtWidgets.QFileDialog.getOpenFileName(self.window, directory=str(Path.home()), filter='zip packed clips(*.zip);;All files(*.*)')[0]
if file:
with ZipFile(file, 'r', compression=ZIP_DEFLATED) as archive:
found = False
for member in archive.namelist():
if member.startswith('clips/'):
found = True
break
if found:
free_size = shutil.disk_usage(os.path.abspath(os.sep))[2]
uncompressed_size = sum([info.file_size for info in archive.filelist])
if uncompressed_size > free_size:
zip_too_big_error = QtWidgets.QErrorMessage(self.window)
zip_too_big_error.showMessage(
f'Cannot add clips from \'{file}\': The uncompressed size ({uncompressed_size * 6000000}MB) '
f'is bigger than the available space on the disk ({free_size * 6000000}MB)')
else:
clips_path = utils.AthnosPath.athnos_path()
for file in archive.namelist():
if file.startswith('clips/'):
final_file = os.path.join(clips_path, file)
if os.path.exists(final_file):
logger.warning(f'Skipping file {final_file}: The file already exists')
else:
archive.extract(file, clips_path)
def on_json_add(self) -> None:
file = QtWidgets.QFileDialog.getOpenFileName(self.window, directory=str(Path.home()), filter='json(*.json);;All files(*.*)')[0]
if file:
try:
clips = json.load(open(file, 'r'))
except json.decoder.JSONDecodeError:
logger.warning(f'{file} is not a json file or has a bad syntax')
json_error = QtWidgets.QErrorMessage(self.window)
json_error.showMessage(f'{file} is not a json file or is corrupt / has a bad syntax')
return
try:
to_add = []
duplicated_files = 0
files_not_found = 0
source_not_found = 0
root_dir = clips['root_dir']
for clip_info in clips['clips']:
path = clip_info['path']
source = clip_info['source']
type_ = clip_info['type']
name = clip_info['name']
description = clip_info['description']
if not os.path.isfile(path):
files_not_found += 1
# logger.warning(f'Skipping file {file}: The file does not exist')
elif utils.AthnosPath.clips_path().joinpath(utils.remove_prefix(path, root_dir)).is_file():
duplicated_files += 1
# logger.warning(f'Skipping file {file}: The file already exist in the destination directory')
else:
if isinstance(source, int):
if not self.database.Source.has_id(source):
clip_info['source'] = 0
source_not_found += 1
elif ''.join(str(value) for value in source.values()) == '':
clip_info['source'] = 0
else:
source_id = self.database.Source.get_by(path=source['path'], type=source['type'], name=source['name'], description=source['description'],
season=source['season'], episode=source['episode'])
if source_id:
clip_info['source'] = source_id
to_add.append(clip_info)
except TypeError as e:
error_message = QtWidgets.QMessageBox(self.window)
error_message.warning(self.window, 'Syntax error', f'Syntax error in {file}\n{str(e)}')
# logger.warning(f'Syntax error in {file}')
return
warning_string = ''
if duplicated_files:
warning_string += f'Duplicated files: {duplicated_files}\n'
if files_not_found:
warning_string += f'Files not found: {files_not_found}\n'
if source_not_found:
warning_string += f'Source not found: {source_not_found}\n'
if warning_string:
warning_string += '\nDo you want to proceed?'
warning_message = QtWidgets.QMessageBox()
reply = warning_message.warning(self.window,
f'{duplicated_files + files_not_found + source_not_found} warnings',
warning_string,
buttons=QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Yes,
defaultButton=QtWidgets.QMessageBox.Yes)
if reply != QtWidgets.QMessageBox.Yes:
return
# iterate two times through the (nearly) same list is pretty inefficient,but for the warning message above it's necessary
for clip_info in to_add:
if isinstance(clip_info['source'], int):
source_id = clip_info['source']
else:
source = clip_info['source']
source_id = self.database.Source.add_source(source['path'], source['type'], source['name'], source['description'], source['season'], source['episode'])
self.database.Clip.add_clip(clip_info['path'], source_id, clip_info['name'], clip_info['type'], clip_info['description'])
logger.debug(f'Added {len(to_add)} clips from json to database')
# --- settings menu ---
def on_new_style(self, checked: bool, action: QtWidgets.QAction) -> None:
"""Gets called if a new style is chosen"""
if checked:
for menu_action in self.style_menu.actions():
if menu_action.isChecked() and menu_action != action:
menu_action.setChecked(False)
break
# changes the style in the config
config['style'] = action.text()
# changes the style on the ui
self.application.setStyle(action.text())
else:
action.setChecked(True)
# --- table view specific ---
def on_search_input_change(self, text: str) -> None:
"""This gets called if the search input text has changed"""
if not self.model:
self.load_model()
for i in range(self.model.columnCount()):
# get the column header
header = str(self.model.headerData(i, QtCore.Qt.Horizontal)).lower()
# checks if the header is in the filter items (which are shown in the filter box)
# and if so, it will check if it's marked / checked
if header in self.filter_items_lower and self.filter_rules[self.filter_items_lower.index(header)]:
# »» sets a new regex filter
self.model.setRegExpFilter(self._columns[header], QtCore.QRegExp(f'^{text}', QtCore.Qt.CaseInsensitive))
# refresh the model
self.model.invalidate()
def on_filter_box_change(self, index: QtCore.QModelIndex) -> None:
# the texts for the search input completer
completer_texts = []
placeholder_text = []
# applies the check states of every item in the filter box to `self.filter_rules`
for i in range(self.filter_box_model.rowCount()):
item = self.filter_box_model.item(i, 0)
checked = item.checkState() == QtCore.Qt.Checked
self.filter_rules[i] = checked
if item.flags() == QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled and checked:
placeholder_text.append(item.data(QtCore.Qt.DisplayRole))
completer_texts.extend(self.all_names[i])
if completer_texts:
# sets the input edit completer
search_input_completer = QtWidgets.QCompleter(completer_texts)
search_input_completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.search_input.setCompleter(search_input_completer)
else:
self.search_input.setCompleter(None)
# sets the placeholder text, matching to the filter items
if placeholder_text:
self.search_input.setPlaceholderText(', '.join(placeholder_text) + ', ...')
# re-sets the clip view to apply the filter box changes
self.on_search_input_change(self.search_input.text())
def on_item_clicked(self, index: QtCore.QModelIndex) -> None:
"""
Gets called if a clip / item is from the clip view was clicked
Args:
index: The clicked item
"""
if not self.clip_source_splitter.isEnabled():
self.clip_source_splitter.setEnabled(True)
self.set_extended_clip_view()
self.set_extended_source_view()
self._row_number = index.row()
self.model = index.model()
# gets the clip (and sets it for global usage), source and tags of the clicked item
self._clip = self.database.Clip.get(self._all_ids[self._row_number])
source = self._clip.get_source()
tags = self._clip.get_tags()
# sets the tag edit stuff
self.file_url_edit.setText(self._clip.path)
self.name_edit.setText(self._clip.name)
self.description_edit.setPlainText(self._clip.description)
if self._clip.type == Type.Clip.AUDIO:
self.type_box.setCurrentIndex(0)
elif self._clip.type == Type.Clip.IMAGE:
self.type_box.setCurrentIndex(1)
elif self._clip.type == Type.Clip.VIDEO:
self.type_box.setCurrentIndex(2)
else:
self.type_box.setCurrentIndex(3)
self.tags_edit.setTags([tag.name for tag in tags])
# source edit
self.source_name.setText(source.name)
self.source_season.setText(source.season)
self.source_episode.setText(source.episode)
self.source_description_edit.setPlainText(source.description)
# --- clip / source view ---
def open_file_or_link_event_filter(self, a0: QtWidgets.QWidget, a1: QtCore.QEvent):
if a1.type() == QtCore.QEvent.MouseButtonRelease and a1.button() == QtCore.Qt.LeftButton:
regex = re.compile(r'^www\.|' # www.
r'(?:http|ftp)s?://' # http:// or https://
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain...
r'localhost|' # localhost...
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
r'(?::\d+)?' # optional port
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
if re.match(regex, self.file_url_edit.text()):
url = QtCore.QUrl(self.file_url_edit.text())
else:
url = QtCore.QUrl.fromLocalFile(self.file_url_edit.text())
QtGui.QDesktopServices.openUrl(url)
def set_extended_clip_view(self, enable=False, clear=False) -> None:
"""
Sets the extended clip view to default
Args:
enable: If True, it enables the extended clip view, False if not
clear: If True, all current texts, clips etc. in the edits will be cleared
"""
# clears everything
if clear:
self.file_url_edit.clear()
self.name_edit.clear()
self.description_edit.clear()
self.tags_edit.clear()
self.type_box.setCurrentIndex(3)
# enable / disable everything
self.file_url_edit.setReadOnly(not enable)
self.name_edit.setReadOnly(not enable)
self.description_edit.setReadOnly(not enable)
self.type_box.setEnabled(enable)
self.tags_edit.setEnabled(enable)
self.tags_edit.setEnabled(enable)
self.save_button.setEnabled(enable)
self.delete_button.setEnabled(enable)
self.enable_editing_check_box.setChecked(enable)
def on_save_edited_clip(self) -> None:
"""Saves an edited clip"""
# the clip type
type = Type.Clip.from_name(self.type_box.currentText())
# the tags (names) from the not edited clip
old_tags = self._clip.get_tags()
old_tag_names = [tag.name for tag in old_tags]
new_tags = []
for tag in self.tags_edit.tags():
# if a tag doesn't exist, it will be new created
if tag not in self.all_tag_names:
# creates the new tag and connect it via an item with the clip
self.database.Item.add_item(self._clip.id, self.database.Tag.add_tag(tag).id)
new_tags.append(tag)
elif tag not in old_tag_names:
# adds a new clip-tag connection via an item
self.database.Item.add_item(self._clip.id, self.database.Tag.get_by(name=tag).id)
else:
# removes the clip from the old (not edited) clip tags
del old_tags[old_tag_names.index(tag)]
if new_tags:
self.all_tag_names.extend(new_tags)
self.tags_edit.setTagSuggestions(self.all_tag_names)
for tag in old_tags:
self._clip.remove_tag(tag.id)
# edits the clip itself
self.database.Clip.edit(self._clip.id, name=self.name_edit.text(), description=self.description_edit.toPlainText(), type=type.value)
logger.debug(f'Edited clip - file / url: `{self.file_url_edit.text()}`, type: `{type.name}`, name: `{self.name_edit.text()}`, '
f'description: `{self.description_edit.toPlainText()}`, tags: `{", ".join(self.tags_edit.tags())}`')
# update the clip view model with the new clip data
self.model.setData(self.model.index(self._row_number, self._columns['name']), self.name_edit.text())
self.model.setData(self.model.index(self._row_number, self._columns['description']), self.description_edit.toPlainText())
self.model.submit()
def set_extended_source_view(self, enable=False, clear=False) -> None:
"""
Sets the extended source view to default
Args:
enable: If True, it enables the extended source view, False if not
clear: If True, all current texts etc. in the edits will be cleared
"""
# clears everything
if clear:
self.source_name.clear()
self.source_season.clear()
self.source_episode.clear()
self.source_type_box.setCurrentIndex(3)
# enable / disable everything
self.source_name.setReadOnly(not enable)
self.source_season.setReadOnly(not enable)
self.source_episode.setReadOnly(not enable)
self.source_description_edit.setReadOnly(not enable)
self.source_type_box.setEnabled(enable)
def on_edit_source(self) -> None:
"""Gets called if the 'Edit source' button is clicked"""
# opens the edit source dialog
EditSource(QtWidgets.QDialog(self.window, QtCore.Qt.WindowSystemMenuHint)).show(self._clip.source_id)
# re-sets the source data for the item
self.on_item_clicked(self.clip_view.selectedIndexes()[0])
def on_change_source(self) -> None:
"""Gets called if the 'Change source' button is clicked"""
# opens the change source dialog
source_id = ShowSource(QtWidgets.QDialog(self.window, QtCore.Qt.WindowSystemMenuHint)).select()
if source_id is not None:
# changes the old to the new source id
self.database.Clip.edit(self._clip.id, source_id=source_id)
# re-sets the source data for the item
self.on_item_clicked(self.clip_view.selectedIndexes()[0])
def close(self) -> None:
# closes the database and the window
self.database.close()
super().close()