You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1267 lines
52 KiB
Python
1267 lines
52 KiB
Python
2 years ago
|
from typing import List
|
||
|
|
||
3 years ago
|
import validators
|
||
|
import os
|
||
3 years ago
|
import logging
|
||
3 years ago
|
import re
|
||
2 years ago
|
import json
|
||
4 years ago
|
|
||
|
from PyQt5 import QtWidgets
|
||
|
from PyQt5.QtCore import Qt, QSize, QUrl
|
||
2 years ago
|
from PyQt5.QtGui import QPixmap, QResizeEvent, QKeyEvent, QStandardItemModel, QStandardItem, QMovie, QDesktopServices, \
|
||
|
QIcon, QCursor
|
||
4 years ago
|
from PyQt5 import QtMultimedia
|
||
|
from PyQt5.QtMultimediaWidgets import QVideoWidget
|
||
|
|
||
3 years ago
|
from ArtNet.gui.windows.importer.picture_importer import Ui_MainWindow
|
||
|
from ArtNet.gui.windows.artnet_mainwindow import ArtnetMainWindow
|
||
|
|
||
4 years ago
|
from ArtNet.gui.dialogs.db_connection_dialog.db_dialog import DBDialog
|
||
|
from ArtNet.gui.dialogs.tag_modify_dialog.tag_mod_dialog import TagModifyDialog
|
||
|
from ArtNet.gui.dialogs.tag_select_dialog.tag_select_dialog import TagSelectDialog
|
||
|
from ArtNet.gui.dockers.presence.presence_dock import PresenceDocker
|
||
|
from ArtNet.gui.dialogs.category_modify_dialog.category_mod_dialog import CategoryModDialog
|
||
|
from ArtNet.gui.dialogs.tag_import_dialog.tag_imp_dialog import TagImportDialog
|
||
3 years ago
|
from ArtNet.gui.dialogs.link_input_dialog.link_dialog import LinkInputDialog
|
||
2 years ago
|
from ArtNet.gui.dialogs.collection_select_dialog.collection_sel_dialog import CollectionSelectDialog
|
||
|
from ArtNet.gui.dialogs.collection_modify_dialog.collection_dialog import CollectionModDialog
|
||
4 years ago
|
|
||
|
from ArtNet.web.link_generator import LinkGenerator
|
||
|
|
||
|
|
||
3 years ago
|
class ImporterWindow(ArtnetMainWindow):
|
||
4 years ago
|
UNKNOWN_TITLE = "-Title Unknown-"
|
||
|
UNKNOWN_PRESENCE = "(Not in Database)"
|
||
3 years ago
|
UNKNOWN_LINK = "No Source Available"
|
||
2 years ago
|
NO_COLLECTION = "No Collections"
|
||
4 years ago
|
|
||
4 years ago
|
def __init__(self, main):
|
||
3 years ago
|
super().__init__(main)
|
||
4 years ago
|
|
||
|
self.__pixmap: QPixmap = None
|
||
|
self.__video: QVideoWidget = None
|
||
|
self.__player: QtMultimedia.QMediaPlayer = None
|
||
4 years ago
|
self.__text_player: QtWidgets.QTextEdit = None
|
||
4 years ago
|
self.__showing_video: bool = False
|
||
4 years ago
|
self.__showing_text: bool = False
|
||
4 years ago
|
|
||
|
self.__tmp_imageid_spinbox: int = None
|
||
|
|
||
|
self.presence_docker_open: bool = False
|
||
|
self.presence_docker: PresenceDocker = None
|
||
|
self.curr_art_id: int = None
|
||
|
self.curr_image_title: str = None
|
||
|
self.curr_link: str = None
|
||
|
self.curr_art_path: str = None
|
||
|
self.curr_file_name: str = None
|
||
|
self.curr_presences: list = list()
|
||
|
self.curr_tags: list = list()
|
||
3 years ago
|
self.curr_implied_tags: list = list()
|
||
4 years ago
|
self.curr_tag_aliases: list = list()
|
||
2 years ago
|
self.curr_collections: list = list()
|
||
4 years ago
|
|
||
|
self.__data_changed: bool = False
|
||
|
|
||
|
self.ui = Ui_MainWindow()
|
||
|
self.ui.setupUi(self)
|
||
|
self.main_title = "ArtNet Picture Importer"
|
||
|
|
||
|
self.ui.actionChange_ArtNet_Root_Folder.triggered.connect(self.on_artnet_root_change_clicked)
|
||
|
self.ui.actionChange_Connection_Details.triggered.connect(self.on_db_connection_change_clicked)
|
||
|
self.ui.actionCreate_New_Tag.triggered.connect(self.on_tag_creation_clicked)
|
||
|
self.ui.actionEdit_a_Tag.triggered.connect(self.on_tag_edit_clicked)
|
||
|
self.ui.actionDelete_a_Tag.triggered.connect(self.on_tag_deletion_clicked)
|
||
|
self.ui.actionCreate_New_Category_2.triggered.connect(self.on_category_creation_clicked)
|
||
|
self.ui.actionDelete_a_Category_2.triggered.connect(self.on_category_deletion_clicked)
|
||
3 years ago
|
self.ui.actionArtNet_Browser.triggered.connect(self.on_browser_clicked)
|
||
4 years ago
|
|
||
|
self.ui.imageNumberSpinBox.valueChanged.connect(self.on_image_id_spinbox_changed)
|
||
|
|
||
|
self.ui.next_image_button.clicked.connect(self.on_next_clicked)
|
||
|
self.ui.prev_image_button.clicked.connect(self.on_previous_clicked)
|
||
|
self.ui.save_button.clicked.connect(self.on_save_clicked)
|
||
|
self.ui.import_button.clicked.connect(self.on_import_tags_clicked)
|
||
|
self.ui.prev_unknown_image_button.clicked.connect(self.on_prev_unknown_image_clicked)
|
||
|
self.ui.next_unknown_image_button.clicked.connect(self.on_next_unknown_image_clicked)
|
||
3 years ago
|
self.ui.collections_button.clicked.connect(self.on_collection_button_clicked)
|
||
4 years ago
|
self.ui.delete_button.clicked.connect(self.on_delete_image_clicked)
|
||
3 years ago
|
self.ui.link_button.clicked.connect(self.on_source_link_button_clicked)
|
||
3 years ago
|
|
||
3 years ago
|
self.ui.link_label.linkActivated.connect(self.on_link_label_activated)
|
||
3 years ago
|
self.ui.image_file_label.linkActivated.connect(self.on_link_file_label_activated)
|
||
3 years ago
|
self.ui.image_author_label.linkActivated.connect(self.on_image_author_label_activated)
|
||
4 years ago
|
|
||
|
self.ui.presence_docker_button.clicked.connect(self.toggle_presence_docker)
|
||
|
|
||
|
self.ui.tag_search_bar.textChanged.connect(self.on_tag_search_change)
|
||
|
self.ui.image_title_line.textChanged.connect(self.on_image_title_change)
|
||
3 years ago
|
self.ui.description_edit.textChanged.connect(self.on_description_change)
|
||
4 years ago
|
|
||
3 years ago
|
self.ui.link_label.setText(ImporterWindow.UNKNOWN_LINK)
|
||
4 years ago
|
|
||
3 years ago
|
self.ui.description_edit.setReadOnly(False)
|
||
|
|
||
2 years ago
|
self.ui.image_label.setContextMenuPolicy(Qt.CustomContextMenu)
|
||
|
self.ui.image_label.customContextMenuRequested.connect(self.on_custom_context_menu_requested)
|
||
|
|
||
4 years ago
|
self.on_tag_search_change()
|
||
|
self.center()
|
||
|
|
||
2 years ago
|
if os.path.isfile("./application_icon.png"):
|
||
|
self.setWindowIcon(QIcon("./application_icon.png"))
|
||
|
else:
|
||
|
logging.warning("Didn't find application icon!")
|
||
|
|
||
4 years ago
|
def center(self):
|
||
|
"""
|
||
|
Centers the window in the middle of the screen
|
||
|
|
||
|
Note: actually not the center but a good position due to images changing size!
|
||
|
:return:
|
||
|
"""
|
||
|
screen = QtWidgets.QDesktopWidget().screenGeometry()
|
||
|
size = self.geometry()
|
||
3 years ago
|
self.move(int((screen.width() - size.width()) / 3), int((screen.height() - size.height()) / 5))
|
||
4 years ago
|
|
||
|
def check_save_changes(self):
|
||
|
"""
|
||
|
Check if there were changes to image settings. If yes ask for confirmation to save them.
|
||
|
|
||
|
:return:
|
||
|
"""
|
||
|
if self.data_changed:
|
||
|
answer = QtWidgets.QMessageBox.question(self, "Save Changes?",
|
||
|
"There have been changes. Do you wish to save them?",
|
||
|
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
|
||
|
QtWidgets.QMessageBox.No)
|
||
|
if answer == QtWidgets.QMessageBox.Yes:
|
||
|
if not self.save_changes():
|
||
|
return False
|
||
|
return True
|
||
|
|
||
|
def save_changes(self) -> bool:
|
||
|
"""
|
||
|
Save the changes to image data to the DB.
|
||
|
:return:
|
||
|
"""
|
||
4 years ago
|
new_title = self.curr_image_title
|
||
3 years ago
|
new_description = self.ui.description_edit.toPlainText().strip()
|
||
3 years ago
|
if new_title == self.curr_file_name or len(new_title) == 0 or new_title == ImporterWindow.UNKNOWN_TITLE:
|
||
4 years ago
|
new_title = None
|
||
3 years ago
|
if new_description is None or len(new_description) == 0:
|
||
|
new_description = None
|
||
|
else:
|
||
|
new_description = new_description.strip()
|
||
4 years ago
|
image_data = {
|
||
|
"ID": self.curr_art_id,
|
||
4 years ago
|
"title": new_title,
|
||
4 years ago
|
"authors": self.curr_presences,
|
||
|
"path": self.curr_art_path,
|
||
|
"tags": self.curr_tags,
|
||
|
"link": self.curr_link,
|
||
3 years ago
|
"md5_hash": self.main.get_md5_of_image(self.curr_art_path),
|
||
3 years ago
|
"description": new_description,
|
||
2 years ago
|
"collections": self.curr_collections,
|
||
4 years ago
|
}
|
||
|
|
||
|
for presence in self.curr_presences:
|
||
3 years ago
|
if presence[-1] == ImporterWindow.UNKNOWN_PRESENCE:
|
||
4 years ago
|
msg = QtWidgets.QMessageBox()
|
||
|
msg.setWindowTitle("Invalid Presence Domain")
|
||
|
msg.setInformativeText("You've tried to save with a not working presence entry! " +
|
||
|
"Please add one from the database!")
|
||
|
msg.setIcon(QtWidgets.QMessageBox.Warning)
|
||
|
msg.exec_()
|
||
|
return False
|
||
|
|
||
2 years ago
|
for coll_id, coll_name in self.curr_collections:
|
||
|
msg = QtWidgets.QMessageBox()
|
||
|
msg.setWindowTitle("Could not save collection!")
|
||
|
msg.setInformativeText("The saving and proper selection of collections is not supported as of now!")
|
||
|
msg.setIcon(QtWidgets.QMessageBox.Warning)
|
||
|
msg.exec_()
|
||
|
break
|
||
|
|
||
3 years ago
|
self.main.db_connection.save_image(ID=image_data["ID"], title=image_data["title"],
|
||
3 years ago
|
authors=image_data["authors"], path=image_data["path"],
|
||
|
tags=image_data["tags"], link=image_data["link"],
|
||
2 years ago
|
md5_hash=image_data["md5_hash"], desc=image_data["description"],
|
||
|
collections=image_data["collections"])
|
||
4 years ago
|
|
||
|
self.set_temporary_status_message("Saved {0} ({1}) to ArtNet DB!"
|
||
4 years ago
|
.format(image_data["title"], image_data["ID"]), 5000)
|
||
4 years ago
|
self.update_window_title()
|
||
|
self.data_changed = False
|
||
|
return True
|
||
|
|
||
|
def set_temporary_status_message(self, text: str, duration: int):
|
||
|
"""
|
||
|
Set a temporary status message (bottom left) for the given duration in milliseconds.
|
||
|
:param text:
|
||
|
:param duration:
|
||
|
:return:
|
||
|
"""
|
||
|
self.ui.statusbar.showMessage(text, duration)
|
||
|
|
||
|
def create_presence(self, name: str, domain: str, artist: tuple, link: str):
|
||
|
"""
|
||
|
Create a new Presence Entry with the given data
|
||
|
:param name:
|
||
|
:param domain:
|
||
|
:param artist:
|
||
|
:param link:
|
||
|
:return:
|
||
|
"""
|
||
|
if len(name) == 0 or len(domain) == 0 or artist is None:
|
||
|
return
|
||
3 years ago
|
self.main.db_connection.save_presence(name=name, domain=domain, artist_ID=artist[0], link=link)
|
||
4 years ago
|
|
||
|
def get_authors(self, presence_name: str, presence_domain: str) -> list:
|
||
|
"""
|
||
|
Query a search for the authors fitting the given strings
|
||
|
:param presence_name:
|
||
|
:param presence_domain:
|
||
|
:return: a list of tuples of (presence_name, presence_domain)
|
||
|
"""
|
||
3 years ago
|
return self.main.db_connection.search_fuzzy_presence(presence_name, presence_domain, all_if_empty=True)
|
||
4 years ago
|
|
||
|
def create_artist(self, ID: int, description: str):
|
||
|
"""
|
||
|
Create a new artist with the given data (or update an exisitng one if ID is already taken
|
||
|
:param ID:
|
||
|
:param description:
|
||
|
:return:
|
||
|
"""
|
||
3 years ago
|
self.main.db_connection.save_artist(ID, description)
|
||
4 years ago
|
self.set_temporary_status_message("Created Artist {0}!".format(description), 3000)
|
||
|
|
||
|
def get_artists(self, search: str) -> list:
|
||
|
"""
|
||
|
Query a search for the artists fitting the given data best. Search is fuzzy.
|
||
|
:param search: either an ID (int) or the description
|
||
|
:return:
|
||
|
"""
|
||
|
try:
|
||
|
ID_int = int(search)
|
||
|
description = None
|
||
|
except ValueError:
|
||
|
ID_int = None
|
||
|
description = search
|
||
|
|
||
3 years ago
|
return self.main.db_connection.search_fuzzy_artists(ID_int, description)
|
||
4 years ago
|
|
||
|
def get_artist(self, id: int) -> list:
|
||
|
"""
|
||
|
Query for the artist matching id. Returns None if the data does not exactly fit.
|
||
|
:param id:
|
||
|
:return:
|
||
|
"""
|
||
3 years ago
|
return self.main.db_connection.get_artist(id)
|
||
4 years ago
|
|
||
|
def remove_artist(self, id: int):
|
||
|
"""
|
||
|
Delte the given artist from the database.
|
||
|
:param id:
|
||
|
:return:
|
||
|
"""
|
||
3 years ago
|
self.main.db_connection.remove_artist(id)
|
||
4 years ago
|
|
||
|
def get_artist_presences(self, id: int) -> list:
|
||
|
"""
|
||
|
Query for all presences associated with the given artist.
|
||
|
:param id:
|
||
|
:return:
|
||
|
"""
|
||
3 years ago
|
return self.main.db_connection.get_artist_presences(id)
|
||
4 years ago
|
|
||
|
def get_all_artists(self) -> list:
|
||
|
"""
|
||
|
Queries the database for a list of all available arists (not presences).
|
||
|
:return:
|
||
|
"""
|
||
3 years ago
|
return self.main.db_connection.get_all_artists()
|
||
4 years ago
|
|
||
|
def get_presence(self, name: str, domain: str):
|
||
|
"""
|
||
|
Query a search for the presence fitting the data
|
||
|
:param name:
|
||
|
:param domain:
|
||
|
:return:
|
||
|
"""
|
||
3 years ago
|
result = self.main.db_connection.get_presence(name, domain)
|
||
4 years ago
|
|
||
|
return result if len(result) != 0 else None
|
||
|
|
||
|
def remove_presence(self, name: str, domain: str):
|
||
|
"""
|
||
|
Deletes a presence from the database and removes all Art_Author entries containing this presence.
|
||
|
:param name:
|
||
|
:param domain:
|
||
|
:return:
|
||
|
"""
|
||
3 years ago
|
self.main.db_connection.remove_presence(name, domain)
|
||
4 years ago
|
|
||
|
def get_presences_art(self, name: str, domain: str):
|
||
|
"""
|
||
|
Query a list of art owned by the given presence
|
||
|
:param name:
|
||
|
:param domain:
|
||
|
:return:
|
||
|
"""
|
||
3 years ago
|
return self.main.db_connection.get_presences_art(name, domain)
|
||
4 years ago
|
|
||
|
def get_current_presences(self) -> list:
|
||
|
"""
|
||
|
Get the presences currently associated with the current art
|
||
|
:return:
|
||
|
"""
|
||
|
return self.curr_presences
|
||
|
|
||
|
def set_current_presences(self, presences: list):
|
||
|
"""
|
||
|
Set the presences associated with the current art
|
||
|
:param presences: list of tuples of (name, domain)
|
||
|
"""
|
||
|
if len(presences) > 1:
|
||
|
for name, domain in presences:
|
||
3 years ago
|
if domain == ImporterWindow.UNKNOWN_PRESENCE:
|
||
4 years ago
|
presences.remove((name, domain))
|
||
|
elif len(presences) == 0:
|
||
3 years ago
|
presences = [(self.curr_art_path.split("/")[0], ImporterWindow.UNKNOWN_PRESENCE)]
|
||
4 years ago
|
self.curr_presences = presences
|
||
|
|
||
|
if self.curr_presences is not None:
|
||
|
self.set_presence_label_text(self.curr_presences)
|
||
|
|
||
|
self.data_changed = True
|
||
|
|
||
2 years ago
|
def get_current_collections(self) -> list:
|
||
|
"""
|
||
|
Get the collections currently associated with the current art
|
||
|
"""
|
||
|
return self.curr_collections
|
||
|
|
||
|
def set_current_collections(self, collections: list):
|
||
|
"""
|
||
|
Set the collections associated with the current art
|
||
|
:param collections: List[Tuple[coll_id, coll_name]]
|
||
|
"""
|
||
|
self.curr_collections = collections
|
||
|
if len(collections) == 0:
|
||
|
self.ui.collections_label.setText(ImporterWindow.NO_COLLECTION)
|
||
|
else:
|
||
|
s = ""
|
||
|
for coll_id, coll_name, coll_desc in collections:
|
||
|
s += "<a href=\"{0}\">{1}</a>".format("", f"#{coll_id}:{coll_name}")
|
||
|
# currently not linking to anything
|
||
|
s += "|"
|
||
|
s = s[:-1]
|
||
|
self.ui.collections_label.setText(s)
|
||
|
self.data_changed = True
|
||
|
|
||
4 years ago
|
def get_categories(self, search: str, all_if_empty: bool = False):
|
||
|
"""
|
||
|
Fuzzy Query for categories in the database.
|
||
|
|
||
|
all_if_empty causes an empty search to return all categories instead of none
|
||
|
:param search:
|
||
|
:param all_if_empty:
|
||
|
:return:
|
||
|
"""
|
||
|
if all_if_empty and len(search) == 0:
|
||
3 years ago
|
return self.main.db_connection.get_all_categories()
|
||
4 years ago
|
|
||
3 years ago
|
return self.main.db_connection.search_fuzzy_categories(search)
|
||
4 years ago
|
|
||
2 years ago
|
def create_collection(self, collection_name: str, description: str = None) -> int:
|
||
|
"""
|
||
|
Create a new collection with the given name and description
|
||
|
|
||
|
Returns the assigned ID of the collection
|
||
|
"""
|
||
|
return self.main.db_connection.create_collection(collection_name, description)
|
||
|
|
||
|
def get_collection(self, collection_id: int):
|
||
|
"""
|
||
|
Fetch the collection given by the id. If not present None is returned.
|
||
|
"""
|
||
|
return self.main.db_connection.get_collection(collection_id)
|
||
|
|
||
|
def delete_collection(self, collection_id: int):
|
||
|
"""
|
||
|
Delete the collection given by the id from the database.
|
||
|
"""
|
||
|
self.main.db_connection.delete_collection(collection_id)
|
||
|
|
||
|
def search_collections(self, collection_name: str = None, presence_name: str = None,
|
||
|
presence_domain: str = None, art_name: str = None):
|
||
|
"""
|
||
|
Search for collections that fit the given parameters. All parameters are searched fuzzy.
|
||
|
"""
|
||
|
return self.main.db_connection.search_collections(collection_name=collection_name,
|
||
|
presence_name=presence_name, presence_domain=presence_domain,
|
||
|
art_name=art_name)
|
||
|
|
||
3 years ago
|
def get_image_link_from_label(self) -> str:
|
||
4 years ago
|
"""
|
||
3 years ago
|
Gets the image link from the label if it is a valid link.
|
||
4 years ago
|
|
||
|
Otherwise an empty string
|
||
|
:return:
|
||
|
"""
|
||
3 years ago
|
result = re.match('[ a-zA-Z<=>"]*((https|http)://[a-zA-Z0-9]+\.[a-zA-Z0-9]+/[a-zA-Z0-9/]+)',
|
||
3 years ago
|
self.ui.link_label.text())
|
||
3 years ago
|
if result is not None:
|
||
3 years ago
|
result = result.groups()[0]
|
||
3 years ago
|
else:
|
||
|
result = ""
|
||
|
return result
|
||
4 years ago
|
|
||
|
def set_presence_label_text(self, presences: list):
|
||
|
"""
|
||
|
Set the label listing all current presences and include links if possible.
|
||
|
:param presences:
|
||
|
:return:
|
||
|
"""
|
||
|
s = ""
|
||
|
for name, domain in presences:
|
||
|
full_data = self.get_presence(name, domain)
|
||
|
if full_data is None:
|
||
|
link = ""
|
||
|
else:
|
||
|
name, domain, _, link = full_data[0]
|
||
|
text = name + ":" + domain
|
||
|
if link is None or len(link) == 0: # no link, then just do plain text
|
||
|
hyperlink = text
|
||
|
else:
|
||
|
hyperlink = "<a href=\"{0}\">{1}</a>".format(link, text)
|
||
|
|
||
|
s += hyperlink
|
||
|
s += "|"
|
||
|
|
||
|
s = s[:-1]
|
||
|
self.ui.image_author_label.setText(s)
|
||
|
|
||
3 years ago
|
def set_image_title_link(self, link: str) -> str:
|
||
4 years ago
|
"""
|
||
|
Sets the Image title to a link if there is link data given for this image.
|
||
3 years ago
|
:return: Returns the result that has been set to see if a successful link was constructed
|
||
4 years ago
|
"""
|
||
3 years ago
|
self.ui.link_label.setText(ImporterWindow.UNKNOWN_LINK)
|
||
4 years ago
|
|
||
3 years ago
|
if link is not None and validators.url(link):
|
||
4 years ago
|
self.curr_link = link
|
||
|
self.data_changed = True
|
||
|
hyperlink = "<a href=\"{0}\">{1}</a>".format(link, "Source")
|
||
|
self.ui.link_label.setText(hyperlink)
|
||
3 years ago
|
self.ui.link_label.setToolTip(link)
|
||
4 years ago
|
return link
|
||
3 years ago
|
elif link is None or len(link) == 0:
|
||
4 years ago
|
return ""
|
||
|
else:
|
||
3 years ago
|
self.ui.link_label.setText(ImporterWindow.UNKNOWN_LINK)
|
||
4 years ago
|
self.set_temporary_status_message("Invalid link \"{0}\" detected!".format(link), 5000)
|
||
|
return ""
|
||
|
|
||
|
def get_tag(self, name: str) -> list:
|
||
|
"""
|
||
|
Query a search for the tag to the DB and return the result
|
||
|
:param name:
|
||
|
:return:
|
||
|
"""
|
||
3 years ago
|
return self.main.db_connection.get_tag_by_name(name)
|
||
4 years ago
|
|
||
|
def get_tag_aliases(self, name: str) -> list:
|
||
|
"""
|
||
|
Query a search for the tag's aliases to the DB
|
||
|
|
||
|
Note: Returns all aliases as a list of their IDs
|
||
|
:param name:
|
||
|
:return:
|
||
|
"""
|
||
3 years ago
|
return self.main.db_connection.get_tag_aliases_by_name(name)
|
||
4 years ago
|
|
||
|
def get_tag_implications(self, name: str) -> list:
|
||
|
"""
|
||
|
Query a search for the tag's implications to the DB
|
||
|
:param name:
|
||
|
:return:
|
||
|
"""
|
||
3 years ago
|
return self.main.db_connection.get_tag_implications(name)
|
||
4 years ago
|
|
||
|
def get_tag_search_result(self, name: str) -> list:
|
||
|
"""
|
||
|
Query a search for tags to the DB that are like name
|
||
|
:return:
|
||
|
"""
|
||
3 years ago
|
return self.main.db_connection.search_fuzzy_tag(name, all_if_empty=True)
|
||
4 years ago
|
|
||
2 years ago
|
def set_tag_search_result_list(self, tags: List[dict]):
|
||
4 years ago
|
"""
|
||
|
Set the tags in the search result list to tags
|
||
|
:param tags:
|
||
|
:return:
|
||
|
"""
|
||
2 years ago
|
tags.sort(key=lambda x: x["name"])
|
||
4 years ago
|
item_model = QStandardItemModel(self.ui.search_result_list)
|
||
|
|
||
|
for tag in tags:
|
||
2 years ago
|
tag_name = tag["name"]
|
||
|
item = QStandardItem(tag_name)
|
||
4 years ago
|
flags = Qt.ItemIsEnabled
|
||
|
|
||
2 years ago
|
if tag_name not in self.curr_implied_tags:
|
||
3 years ago
|
# new tag and not implied yet
|
||
4 years ago
|
item.setData(Qt.Unchecked, Qt.CheckStateRole)
|
||
2 years ago
|
if 'id' in tag.keys() and 'description' in tag.keys() and 'category' in tag.keys():
|
||
|
s = f"Tag ID: {tag['id']}\n\n"
|
||
|
if len(tag["description"]) != 0:
|
||
|
s += tag["description"]+"\n\n"
|
||
|
s += f"Category: {tag['category']}"
|
||
|
item.setToolTip(s)
|
||
|
item.setData(json.dumps(tag), Qt.UserRole)
|
||
4 years ago
|
flags |= Qt.ItemIsUserCheckable
|
||
2 years ago
|
|
||
|
is_in_tags = False
|
||
|
for i in range(len((self.curr_tags + self.curr_implied_tags + self.curr_tag_aliases))):
|
||
|
if item.text() == (self.curr_tags + self.curr_implied_tags + self.curr_tag_aliases)[i]["name"]:
|
||
|
is_in_tags = True
|
||
|
|
||
|
if self.curr_tags is not None and is_in_tags:
|
||
4 years ago
|
# already selected, implied or aliased tags
|
||
|
item.setCheckState(Qt.Checked)
|
||
|
item.setFlags(flags)
|
||
|
item_model.appendRow(item)
|
||
|
|
||
|
item_model.itemChanged.connect(self.on_tag_search_item_changed)
|
||
|
self.ui.search_result_list.setModel(item_model)
|
||
|
|
||
3 years ago
|
def set_description_text(self, text: str):
|
||
|
"""
|
||
|
Set the text for the description field
|
||
|
"""
|
||
|
self.ui.description_edit.setText(text)
|
||
|
|
||
4 years ago
|
def set_tag_list(self, tags: list, set_checked: bool = True, no_implication: bool = False):
|
||
|
"""
|
||
|
Set the tags in the tag list to this.
|
||
|
Also updates the tag implication list if no_implication is False
|
||
|
:param tags:
|
||
|
:param set_checked:
|
||
2 years ago
|
:param no_implication: not used
|
||
4 years ago
|
:return:
|
||
|
"""
|
||
2 years ago
|
tags.sort(key=lambda x: x["name"])
|
||
4 years ago
|
self.curr_tags = tags
|
||
|
item_model = QStandardItemModel(self.ui.tag_list)
|
||
|
|
||
2 years ago
|
implied_tags = []
|
||
|
for x in self.curr_tags:
|
||
|
# collect all implied tags into a list
|
||
2 years ago
|
implied_tags += self.main.db_connection.get_all_tag_implications_by_name(x["name"])
|
||
2 years ago
|
|
||
|
self.set_implied_list(implied_tags)
|
||
|
|
||
4 years ago
|
for tag in tags:
|
||
2 years ago
|
tag_name = tag["name"]
|
||
|
item = QStandardItem(tag_name)
|
||
4 years ago
|
|
||
2 years ago
|
flags = Qt.ItemIsEnabled
|
||
2 years ago
|
if tag_name not in self.curr_implied_tags:
|
||
2 years ago
|
# new tag and not implied yet
|
||
|
item.setData(Qt.Unchecked, Qt.CheckStateRole)
|
||
2 years ago
|
if 'id' in tag.keys() and 'description' in tag.keys() and 'category' in tag.keys():
|
||
|
s = f"Tag ID: {tag['id']}\n\n"
|
||
|
if len(tag["description"]) != 0:
|
||
|
s += tag["description"] + "\n\n"
|
||
|
s += f"Category: {tag['category']}"
|
||
|
item.setToolTip(s)
|
||
2 years ago
|
flags |= Qt.ItemIsUserCheckable
|
||
4 years ago
|
|
||
|
if set_checked:
|
||
2 years ago
|
if tag_name not in self.curr_implied_tags:
|
||
2 years ago
|
item.setCheckState(Qt.Checked)
|
||
|
else:
|
||
|
item.setCheckState(Qt.Unchecked)
|
||
|
|
||
|
item.setFlags(flags)
|
||
|
item_model.appendRow(item)
|
||
4 years ago
|
|
||
|
item_model.itemChanged.connect(self.on_tag_item_changed)
|
||
|
self.ui.tag_list.setModel(item_model)
|
||
|
|
||
|
self.data_changed = True
|
||
|
|
||
|
def set_implied_list(self, tags: list):
|
||
|
"""
|
||
|
Sets the implied tags in the imply list
|
||
|
:param tags:
|
||
|
:return:
|
||
|
"""
|
||
2 years ago
|
self.curr_implied_tags = tags
|
||
4 years ago
|
item_model = QStandardItemModel(self.ui.implied_tag_list)
|
||
|
done = []
|
||
|
for tag in tags:
|
||
2 years ago
|
tag_name = tag["name"]
|
||
|
if tag_name in done:
|
||
4 years ago
|
continue
|
||
|
else:
|
||
2 years ago
|
done.append(tag_name)
|
||
|
item = QStandardItem(tag_name)
|
||
2 years ago
|
if 'id' in tag.keys() and 'description' in tag.keys() and 'category' in tag.keys():
|
||
|
s = f"Tag ID: {tag['id']}\n\n"
|
||
|
if len(tag["description"]) != 0:
|
||
|
s += tag["description"] + "\n\n"
|
||
|
s += f"Category: {tag['category']}"
|
||
|
item.setToolTip(s)
|
||
4 years ago
|
item_model.appendRow(item)
|
||
|
|
||
|
self.ui.implied_tag_list.setModel(item_model)
|
||
|
|
||
|
self.data_changed = True
|
||
|
|
||
|
def display_image(self, image_title: str, image_authors: list, full_path: str, relative_path: str, art_ID: int,
|
||
2 years ago
|
link: str, file_name: str, description: str, collections: list):
|
||
4 years ago
|
"""
|
||
|
Display an image in the central widget
|
||
|
:param image_authors:
|
||
|
:param image_title:
|
||
|
:param full_path:
|
||
|
:param relative_path:
|
||
|
:param art_ID:
|
||
|
:param link:
|
||
|
:param file_name:
|
||
3 years ago
|
:param description:
|
||
2 years ago
|
:param collections:
|
||
4 years ago
|
:return:
|
||
|
"""
|
||
|
self.curr_art_id = art_ID
|
||
|
self.curr_art_path = relative_path
|
||
|
self.curr_image_title = image_title
|
||
|
self.curr_file_name = os.path.basename(full_path)
|
||
|
self.curr_link = link
|
||
2 years ago
|
|
||
|
self.set_current_collections(collections)
|
||
4 years ago
|
self.set_current_presences(image_authors)
|
||
|
|
||
|
file_ending = relative_path.split(".")[-1]
|
||
|
if self.__showing_video: # remove old video from image layout
|
||
3 years ago
|
# self.ui.image_frame.layout().removeWidget(self.__video)
|
||
4 years ago
|
self.__video.hide()
|
||
|
self.ui.image_label.show()
|
||
4 years ago
|
if self.__showing_text: # remove text are from image layout
|
||
|
self.__text_player.hide()
|
||
|
self.ui.image_label.show()
|
||
|
|
||
4 years ago
|
if file_ending in ["gif"]:
|
||
|
self.__showing_video = False
|
||
|
self.__pixmap = QMovie(full_path)
|
||
|
self.ui.image_label.setMovie(self.__pixmap)
|
||
|
self.__pixmap.start()
|
||
|
self.__pixmap.frameChanged.connect(self.on_movie_frame_changed)
|
||
3 years ago
|
elif file_ending in ["webm", "mp4", "mov", "mkv"]:
|
||
4 years ago
|
self.__showing_video = True
|
||
3 years ago
|
if isinstance(self.__video, QVideoWidget):
|
||
|
self.__video.show()
|
||
|
else:
|
||
|
self.__video = QVideoWidget() if self.__video is None else self.__video
|
||
4 years ago
|
self.__player = QtMultimedia.QMediaPlayer(None, QtMultimedia.QMediaPlayer.VideoSurface)
|
||
|
self.__player.setVideoOutput(self.__video)
|
||
|
self.__player.setMedia(QtMultimedia.QMediaContent(QUrl.fromLocalFile(full_path)))
|
||
|
self.__player.play()
|
||
|
|
||
|
self.ui.image_frame.layout().addWidget(self.__video)
|
||
|
self.ui.image_label.hide()
|
||
|
|
||
|
self.__player.stateChanged.connect(self.on_movie_player_state_changed)
|
||
|
self.__player.positionChanged.connect(self.on_movie_position_changed)
|
||
4 years ago
|
elif file_ending in ["txt"]: # for stories or text files
|
||
|
self.__showing_text = True
|
||
|
self.ui.image_label.hide()
|
||
|
self.__text_player = QtWidgets.QTextEdit() if self.__text_player is None else self.__text_player
|
||
|
|
||
|
with open(full_path, "r") as text_file:
|
||
|
story = text_file.read()
|
||
|
text_file.close()
|
||
|
|
||
|
self.__text_player.setText(story)
|
||
|
self.__text_player.setReadOnly(True)
|
||
|
|
||
|
self.ui.image_frame.layout().addWidget(self.__text_player)
|
||
|
self.__text_player.show()
|
||
|
|
||
4 years ago
|
else:
|
||
|
self.__showing_video = False
|
||
|
self.__pixmap = QPixmap(full_path)
|
||
|
self.ui.image_label.setPixmap(self.__pixmap)
|
||
|
|
||
|
self.ui.image_label.setScaledContents(True)
|
||
|
self.ui.image_label.setFixedSize(0, 0)
|
||
|
self.__image_resize()
|
||
|
|
||
|
self.ui.image_label.setAlignment(Qt.AlignCenter)
|
||
|
self.ui.image_title_line.setText(image_title)
|
||
|
self.update_window_title()
|
||
3 years ago
|
|
||
|
self.ui.image_file_label.setText("<a href=\"{0}\" style=\"color: white; text-decoration: none\">{1}</a>"
|
||
|
.format(f"file:///{full_path[:-1 * len(self.curr_file_name)]}",
|
||
|
file_name))
|
||
|
self.ui.image_file_label.setToolTip(relative_path)
|
||
3 years ago
|
self.ui.description_edit.setText(description)
|
||
3 years ago
|
self.set_image_title_link(link)
|
||
4 years ago
|
self.set_image_id_spinbox()
|
||
|
|
||
3 years ago
|
self.data_changed = False # reset any triggered change detection
|
||
|
|
||
4 years ago
|
def update_window_title(self):
|
||
|
"""
|
||
|
Update the title of the window with the newest image title as given in text field
|
||
|
:return:
|
||
|
"""
|
||
|
image_title = self.ui.image_title_line.text()
|
||
|
self.setWindowTitle(self.main_title + " - " + image_title
|
||
3 years ago
|
+ f" ({round(self.main.known_image_amount / len(self.main.all_images), 5)}%)")
|
||
4 years ago
|
|
||
|
def set_image_id_spinbox(self):
|
||
|
"""
|
||
|
Sets the imageIDSpinBox to the image ID of the currently displayed image
|
||
|
:return:
|
||
|
"""
|
||
|
self.ui.imageNumberSpinBox.setMinimum(0)
|
||
3 years ago
|
self.ui.imageNumberSpinBox.setMaximum(len(self.main.all_images) - 1)
|
||
3 years ago
|
self.ui.imageNumberSpinBox.setValue(self.main.curr_image_index)
|
||
4 years ago
|
|
||
|
def __image_resize(self):
|
||
|
"""
|
||
|
Resize the given pixmap so that we're not out of the desktop.
|
||
|
|
||
|
:return: new scaled QPixmap
|
||
|
"""
|
||
|
if self.ui.image_label.movie() is not None or self.__showing_video: # if QMovie was used instead of image
|
||
|
rect = self.geometry()
|
||
|
size = QSize(min(rect.width(), rect.height()), min(rect.width(), rect.height()))
|
||
|
if type(self.__pixmap) != QMovie: # using QVideoWidget?
|
||
|
pass
|
||
3 years ago
|
# self.__player.setScaledSize(size)
|
||
4 years ago
|
else:
|
||
|
self.__pixmap.setScaledSize(size)
|
||
|
return
|
||
|
|
||
|
size = self.__pixmap.size()
|
||
|
screen_rect = QtWidgets.QDesktopWidget().screenGeometry()
|
||
|
|
||
|
size.scale(int(screen_rect.width() * 0.6), int(screen_rect.height() * 0.6),
|
||
|
Qt.KeepAspectRatio)
|
||
|
self.ui.image_label.setFixedSize(size)
|
||
|
|
||
|
def resizeEvent(self, a0: QResizeEvent) -> None:
|
||
|
self.__image_resize()
|
||
|
|
||
|
def keyPressEvent(self, a0: QKeyEvent) -> None:
|
||
3 years ago
|
super(ImporterWindow, self).keyPressEvent(a0)
|
||
4 years ago
|
if a0.key() == Qt.Key_Left:
|
||
|
self.on_previous_clicked()
|
||
|
elif a0.key() == Qt.Key_Right:
|
||
|
self.on_next_clicked()
|
||
|
elif a0.key() == Qt.Key_Return:
|
||
3 years ago
|
logging.debug("Pressed Enter!")
|
||
4 years ago
|
if self.__showing_video:
|
||
|
s = self.__player.state()
|
||
|
if self.__player.state() == QtMultimedia.QMediaPlayer.PlayingState:
|
||
|
self.__player.pause()
|
||
|
self.set_temporary_status_message("Paused the Video!", 3000)
|
||
|
elif self.__player.state() == QtMultimedia.QMediaPlayer.PausedState:
|
||
|
self.__player.play()
|
||
|
self.set_temporary_status_message("Started the Video!", 3000)
|
||
|
elif self.__player.state() == QtMultimedia.QMediaPlayer.StoppedState:
|
||
|
self.__player.play()
|
||
|
self.set_temporary_status_message("Restarted the Video!", 3000)
|
||
|
|
||
|
elif type(self.__pixmap) == QMovie:
|
||
|
if self.__pixmap.state() == QMovie.Paused:
|
||
|
self.__pixmap.start()
|
||
|
self.set_temporary_status_message("Started the Video!", 3000)
|
||
|
elif self.__pixmap.state() == QMovie.Running:
|
||
|
self.__pixmap.setPaused(True)
|
||
|
self.set_temporary_status_message("Paused the Video!", 3000)
|
||
|
|
||
3 years ago
|
elif QtWidgets.QApplication.focusWidget() == self.ui.tag_search_bar: # quick select for the search bar
|
||
|
|
||
|
if self.ui.search_result_list.model().rowCount() == 1: # only 1 search result left
|
||
|
model_index = self.ui.search_result_list.model().index(0, 0)
|
||
2 years ago
|
item_data = json.loads(self.ui.search_result_list.model().itemData(model_index)[Qt.UserRole])
|
||
3 years ago
|
|
||
2 years ago
|
if item_data not in self.curr_tags: # add/remove new selected tag to the lists
|
||
|
self.curr_tags.append(item_data)
|
||
3 years ago
|
else:
|
||
2 years ago
|
self.curr_tags.remove(item_data)
|
||
3 years ago
|
|
||
|
self.set_tag_list(self.curr_tags) # update relevant lists
|
||
2 years ago
|
self.set_tag_search_result_list([item_data])
|
||
3 years ago
|
logging.debug(item_data)
|
||
3 years ago
|
|
||
4 years ago
|
def on_movie_player_state_changed(self, state: int):
|
||
|
self.__image_resize()
|
||
|
if QtMultimedia.QMediaPlayer.StoppedState == state: # player stopped
|
||
|
self.set_temporary_status_message("Reached end of Video!", 2000)
|
||
|
|
||
|
def on_movie_position_changed(self, position):
|
||
|
pass
|
||
|
|
||
|
def on_movie_frame_changed(self, frame_number: int):
|
||
|
if type(self.__pixmap) != QMovie:
|
||
|
return
|
||
|
if frame_number == 0:
|
||
|
self.set_temporary_status_message("Reached end of Video!", 2000)
|
||
|
self.__pixmap.setPaused(True)
|
||
|
|
||
|
def on_save_clicked(self):
|
||
3 years ago
|
logging.info("Clicked Save!")
|
||
4 years ago
|
self.save_changes()
|
||
|
|
||
|
def on_import_tags_clicked(self):
|
||
3 years ago
|
logging.info("Clicked Import!")
|
||
4 years ago
|
dialog = TagImportDialog(self)
|
||
3 years ago
|
if len(self.get_image_link_from_label()) == 0 \
|
||
|
or self.get_image_link_from_label() == ImporterWindow.UNKNOWN_LINK:
|
||
4 years ago
|
url = LinkGenerator.get_instance().construct_link(self.curr_file_name,
|
||
|
LinkGenerator.get_instance()
|
||
|
.predict_domain(self.curr_file_name))
|
||
3 years ago
|
self.set_image_title_link(url) # Update no link to the predicted link
|
||
4 years ago
|
else:
|
||
3 years ago
|
url = self.get_image_link_from_label()
|
||
4 years ago
|
|
||
3 years ago
|
r = self.main.scrape_tags(self.curr_file_name, url=url,
|
||
3 years ago
|
art_ID=self.curr_art_id)
|
||
4 years ago
|
|
||
|
if r is None:
|
||
|
msg = QtWidgets.QMessageBox()
|
||
|
msg.setWindowTitle("Unsupported Domain")
|
||
|
msg.setInformativeText("Could not predict a supported domain!")
|
||
|
msg.setIcon(QtWidgets.QMessageBox.Warning)
|
||
|
msg.exec_()
|
||
|
return
|
||
|
|
||
3 years ago
|
self.set_image_title_link(url)
|
||
4 years ago
|
tags, artists = r
|
||
|
|
||
|
i = 0
|
||
|
while i < len(tags): # workaround for an issue with altering lists during iteration
|
||
3 years ago
|
r = self.main.db_connection.get_tag_by_name(tags[i])
|
||
2 years ago
|
if r is not None:
|
||
|
self.curr_tags.append(r)
|
||
4 years ago
|
self.data_changed = True
|
||
|
tags.remove(tags[i])
|
||
|
continue
|
||
|
else:
|
||
|
i += 1
|
||
|
self.set_tag_list(self.curr_tags)
|
||
|
|
||
|
if len(tags) == 0:
|
||
|
msg = QtWidgets.QMessageBox()
|
||
|
msg.setWindowTitle("Nothing to import!")
|
||
|
msg.setInformativeText("There were no tags to import for this art!")
|
||
|
msg.setIcon(QtWidgets.QMessageBox.Information)
|
||
|
msg.exec_()
|
||
|
return
|
||
|
dialog.set_import_tag_list(tags)
|
||
|
dialog.set_detected_artists(artists)
|
||
|
dialog.set_used_link(url)
|
||
|
dialog.to_import = tags
|
||
|
|
||
|
result = dialog.exec_()
|
||
|
if result is None:
|
||
|
self.set_tag_list(self.curr_tags)
|
||
|
return
|
||
|
|
||
3 years ago
|
self.main.import_tags(result)
|
||
4 years ago
|
self.set_tag_list(self.curr_tags)
|
||
|
|
||
|
def on_next_clicked(self):
|
||
3 years ago
|
logging.info("Clicked Next!")
|
||
4 years ago
|
if not self.check_save_changes():
|
||
|
return
|
||
|
|
||
3 years ago
|
self.main.curr_image_index += 1
|
||
|
if self.main.curr_image_index >= len(self.main.all_images):
|
||
|
self.main.curr_image_index = 0
|
||
|
self.main.refresh_shown_image()
|
||
4 years ago
|
|
||
|
if self.presence_docker_open:
|
||
|
self.toggle_presence_docker()
|
||
|
self.on_tag_search_change()
|
||
|
|
||
|
def on_image_title_change(self):
|
||
|
self.data_changed = True
|
||
|
self.curr_image_title = self.ui.image_title_line.text()
|
||
|
|
||
|
def on_previous_clicked(self):
|
||
3 years ago
|
logging.info("Clicked previous!")
|
||
4 years ago
|
if not self.check_save_changes():
|
||
|
return
|
||
|
|
||
3 years ago
|
self.main.curr_image_index -= 1
|
||
|
if self.main.curr_image_index < 0:
|
||
|
self.main.curr_image_index += len(self.main.all_images)
|
||
4 years ago
|
|
||
3 years ago
|
self.main.refresh_shown_image()
|
||
4 years ago
|
|
||
|
if self.presence_docker_open:
|
||
|
self.toggle_presence_docker()
|
||
|
self.on_tag_search_change()
|
||
|
|
||
|
def toggle_presence_docker(self):
|
||
|
if not self.presence_docker_open:
|
||
3 years ago
|
logging.info("Opened presence docker!")
|
||
4 years ago
|
self.presence_docker = PresenceDocker(self)
|
||
|
self.ui.presence_docker_layout.addWidget(self.presence_docker)
|
||
|
self.presence_docker.set_selected_presences_list(self.get_current_presences())
|
||
3 years ago
|
self.ui.presence_docker_button.setArrowType(Qt.RightArrow)
|
||
4 years ago
|
self.presence_docker_open = True
|
||
|
else:
|
||
3 years ago
|
logging.info("Closed presence docker!")
|
||
4 years ago
|
self.presence_docker.setParent(None)
|
||
3 years ago
|
self.ui.presence_docker_button.setArrowType(Qt.LeftArrow)
|
||
4 years ago
|
|
||
|
self.presence_docker.destroy()
|
||
|
self.presence_docker = None
|
||
|
self.presence_docker_open = False
|
||
|
|
||
|
def on_artnet_root_change_clicked(self):
|
||
3 years ago
|
logging.info("Clicked changing ArtNet root!")
|
||
4 years ago
|
dialog = QtWidgets.QFileDialog(self, 'Choose new ArtNet root:')
|
||
|
dialog.setFileMode(QtWidgets.QFileDialog.Directory)
|
||
|
dialog.setOptions(QtWidgets.QFileDialog.ShowDirsOnly)
|
||
|
|
||
|
directory = dialog.getExistingDirectory()
|
||
3 years ago
|
self.main.change_root(directory)
|
||
4 years ago
|
|
||
|
def on_db_connection_change_clicked(self):
|
||
3 years ago
|
logging.info("Clicked db connection change!")
|
||
4 years ago
|
dialog = DBDialog(self)
|
||
3 years ago
|
prev_db_data = self.main.get_db_connection_details()
|
||
4 years ago
|
dialog.ui.user_line_edit.setText(prev_db_data["user"])
|
||
|
dialog.ui.password_line_edit.setText(prev_db_data["password"])
|
||
|
dialog.ui.host_line_edit.setText(prev_db_data["host"])
|
||
|
dialog.ui.database_line_edit.setText(prev_db_data["database"])
|
||
|
dialog.ui.port_line_edit.setText(str(prev_db_data["port"]))
|
||
|
|
||
|
db_data: dict = dialog.exec_()
|
||
|
if len(db_data.keys()) == 0:
|
||
|
return
|
||
3 years ago
|
try:
|
||
|
self.main.change_db_connection(host=db_data["host"], port=db_data["port"],
|
||
|
user=db_data["user"], password=db_data["password"],
|
||
|
database=db_data["database"])
|
||
|
except ValueError as e: # details were wrong (probably)
|
||
|
QtWidgets.QMessageBox.warning(self, "Wrong Credentials?", f"Error: {e}")
|
||
4 years ago
|
|
||
|
def on_tag_creation_clicked(self):
|
||
3 years ago
|
logging.info("Clicked Tag Creation!")
|
||
4 years ago
|
dialog = TagModifyDialog(self, create_tag=True)
|
||
|
|
||
|
tag_data: dict = dialog.exec_()
|
||
3 years ago
|
logging.debug(f"Got Tag data: {tag_data}")
|
||
4 years ago
|
if tag_data is None or len(tag_data.keys()) == 0: # got canceled?
|
||
|
return
|
||
|
|
||
2 years ago
|
if self.get_tag(tag_data["name"]) is not None: # check if tag exists
|
||
4 years ago
|
QtWidgets.QMessageBox.information(self, "Duplicate Tag", "The tag \"{0}\" already exists in the db!"
|
||
|
.format(tag_data["name"]))
|
||
|
return
|
||
3 years ago
|
self.main.db_connection.create_tag(name=tag_data["name"], description=tag_data["description"],
|
||
3 years ago
|
aliases=tag_data["aliases"], implications=tag_data["implications"],
|
||
|
category=tag_data["category"])
|
||
4 years ago
|
self.on_tag_search_change()
|
||
|
|
||
2 years ago
|
implied_tags = [] # refresh implied tags, the edit/addition might have changed smth
|
||
|
for x in self.curr_tags:
|
||
|
# collect all implied tags into a list
|
||
|
implied_tags += self.main.db_connection.get_all_tag_implications_by_name(x)
|
||
|
|
||
4 years ago
|
def on_tag_deletion_clicked(self):
|
||
3 years ago
|
logging.info("Clicked Tag Deletion!")
|
||
4 years ago
|
dialog = TagSelectDialog(self, delete_tag=True)
|
||
|
|
||
|
tag = dialog.exec_()
|
||
3 years ago
|
logging.debug("Got Tag", tag)
|
||
4 years ago
|
if tag is None or len(tag) == 0:
|
||
|
return
|
||
|
|
||
|
confirmation_reply = QtWidgets.QMessageBox.question(self, "Delete Tag \"{0}\"".format(tag["name"]),
|
||
|
"Are you sure you want to delete this Tag?",
|
||
|
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
|
||
|
QtWidgets.QMessageBox.No)
|
||
|
if confirmation_reply == QtWidgets.QMessageBox.No:
|
||
|
return
|
||
|
|
||
3 years ago
|
self.main.db_connection.remove_tag_by_name(tag["name"])
|
||
4 years ago
|
self.on_tag_search_change()
|
||
|
|
||
|
def force_edit_tag_dialog(self, name: str):
|
||
|
|
||
|
edit_dialog = TagModifyDialog(self, create_tag=True)
|
||
|
edit_dialog.ui.tag_name_line.setText(name)
|
||
|
edit_dialog.ui.tag_description_area.setText("")
|
||
|
edit_dialog.set_selected_alias_tags([], set_checked=True)
|
||
|
edit_dialog.alias_selection = []
|
||
|
edit_dialog.set_selected_implicated_tags([], set_checked=True)
|
||
|
edit_dialog.implication_selection = []
|
||
|
edit_dialog.category_selection = []
|
||
|
edit_dialog.set_all_categories()
|
||
|
|
||
|
tag_data = edit_dialog.exec_()
|
||
3 years ago
|
logging.debug(f"Got Tag data: {tag_data}")
|
||
4 years ago
|
if tag_data is None or len(tag_data.keys()) == 0:
|
||
|
return None
|
||
|
if len(tag_data["category"]) == 0:
|
||
|
answer = QtWidgets.QMessageBox.information(self, "No Category",
|
||
|
"There has been no Category selected for this tag! "
|
||
|
"No tag is allowed without a category!")
|
||
|
return None
|
||
|
|
||
2 years ago
|
if self.get_tag(tag_data['name']) is not None:
|
||
4 years ago
|
QtWidgets.QMessageBox.information(self, "Tag already exists",
|
||
2 years ago
|
f"The Tag \"{tag_data['name']}\" you wanted to create already exists! Skipping...")
|
||
|
return None
|
||
4 years ago
|
else:
|
||
3 years ago
|
self.main.db_connection.create_tag(name=tag_data["name"], description=tag_data["description"],
|
||
3 years ago
|
aliases=tag_data["aliases"], implications=tag_data["implications"],
|
||
|
category=tag_data["category"])
|
||
4 years ago
|
self.on_tag_search_change()
|
||
|
return tag_data
|
||
|
|
||
|
def on_tag_edit_clicked(self):
|
||
3 years ago
|
logging.info("Clicked Tag Editing!")
|
||
4 years ago
|
select_dialog = TagSelectDialog(self, delete_tag=False)
|
||
|
|
||
|
tag = select_dialog.exec_()
|
||
|
if tag is None or len(tag) == 0:
|
||
|
return
|
||
|
|
||
2 years ago
|
#tag['aliases'] = self.main.db_connection.get_tag_aliases_by_name(tag["name"])
|
||
|
#tag['implications'] = self.main.db_connection.get_tag_implications(tag["name"])
|
||
4 years ago
|
|
||
|
edit_dialog = TagModifyDialog(self, create_tag=False)
|
||
|
edit_dialog.ui.tag_name_line.setText(tag["name"])
|
||
|
edit_dialog.ui.tag_description_area.setText(tag["description"])
|
||
|
edit_dialog.set_selected_alias_tags(tag["aliases"], set_checked=True)
|
||
|
edit_dialog.alias_selection = tag["aliases"]
|
||
|
edit_dialog.set_selected_implicated_tags(tag["implications"], set_checked=True)
|
||
|
edit_dialog.implication_selection = tag["implications"]
|
||
3 years ago
|
edit_dialog.category_selection = self.main.db_connection.get_category_by_ID(tag["category_id"])[1]
|
||
4 years ago
|
edit_dialog.set_all_categories()
|
||
|
|
||
|
tag_data = edit_dialog.exec_()
|
||
3 years ago
|
logging.debug(f"Got Tag data: {tag_data}")
|
||
4 years ago
|
if tag_data is None or len(tag_data.keys()) == 0:
|
||
|
return
|
||
|
|
||
|
if "old_tag_name" not in tag_data.keys():
|
||
|
tag_data["old_tag_name"] = None
|
||
|
|
||
3 years ago
|
self.main.db_connection.edit_tag(tag_id=tag["ID"],
|
||
3 years ago
|
name=tag_data["name"], description=tag_data["description"],
|
||
|
aliases=tag_data["aliases"], implications=tag_data["implications"],
|
||
|
category_id=self.main.db_connection.get_category_by_name(tag_data["category"])[
|
||
|
0])
|
||
4 years ago
|
self.on_tag_search_change()
|
||
|
|
||
|
def on_tag_search_item_changed(self, item: QStandardItem):
|
||
|
if item.checkState() == Qt.Checked:
|
||
2 years ago
|
tag_data = self.main.db_connection.get_tag_by_name(item.text())
|
||
|
self.curr_tags.append(tag_data)
|
||
2 years ago
|
|
||
|
aliases = self.main.db_connection.get_tag_aliases_by_name(item.text())
|
||
|
for alias in aliases:
|
||
|
self.curr_tags.append(alias)
|
||
|
implications = self.main.db_connection.get_all_tag_implications_by_name(item.text())
|
||
|
for implication in implications:
|
||
|
self.curr_tags.append(implication)
|
||
4 years ago
|
if item.checkState() == Qt.Unchecked:
|
||
2 years ago
|
is_in_tags = False
|
||
|
for i in range(len(self.curr_tags)):
|
||
|
if item.text() == self.curr_tags[i]["name"]:
|
||
|
tags_index = i
|
||
|
is_in_tags = True
|
||
|
if is_in_tags:
|
||
|
self.curr_tags.pop(tags_index)
|
||
2 years ago
|
aliases = self.main.db_connection.get_tag_aliases_by_name(item.text())
|
||
|
for alias in aliases:
|
||
|
if alias in self.curr_tags:
|
||
|
self.curr_tags.remove(alias)
|
||
|
implications = self.main.db_connection.get_all_tag_implications_by_name(item.text())
|
||
|
for implication in implications:
|
||
2 years ago
|
self.curr_tags.remove({"name": implication})
|
||
4 years ago
|
else:
|
||
2 years ago
|
raise Exception("Something went terribly wrong!")
|
||
|
|
||
4 years ago
|
self.set_tag_list(self.curr_tags)
|
||
2 years ago
|
self.on_tag_search_change()
|
||
4 years ago
|
|
||
|
def on_tag_item_changed(self, item: QStandardItem):
|
||
3 years ago
|
logging.debug("Item {0} has changed!".format(item.text()))
|
||
4 years ago
|
if item.checkState() == Qt.Unchecked:
|
||
2 years ago
|
is_in_tags = False
|
||
|
for i in range(len(self.curr_tags)):
|
||
|
if item.text() == self.curr_tags[i]["name"]:
|
||
|
tags_index = i
|
||
|
is_in_tags = True
|
||
|
if is_in_tags:
|
||
2 years ago
|
aliases = self.main.db_connection.get_tag_aliases_by_name(item.text())
|
||
|
if len(aliases) > 0: # tag has aliases, might need to also remove them
|
||
|
for alias in aliases:
|
||
|
self.curr_tags.remove(alias)
|
||
|
|
||
2 years ago
|
for i in range(len(self.curr_tags)):
|
||
|
if item.text() == self.curr_tags[i]["name"]:
|
||
|
tags_index = i
|
||
|
|
||
|
self.curr_tags.pop(tags_index)
|
||
4 years ago
|
self.set_tag_list(self.curr_tags)
|
||
|
self.on_tag_search_change()
|
||
|
else:
|
||
|
raise Exception("Something went terribly wrong!")
|
||
|
|
||
|
def on_tag_search_change(self):
|
||
3 years ago
|
tags = self.main.db_connection.search_fuzzy_tag(self.ui.tag_search_bar.text(), all_if_empty=True)
|
||
2 years ago
|
self.set_tag_search_result_list(tags)
|
||
4 years ago
|
|
||
|
def on_category_creation_clicked(self):
|
||
|
dialog = CategoryModDialog(self, delete_category=False)
|
||
|
|
||
|
data = dialog.exec_()
|
||
|
if data is None:
|
||
|
return
|
||
|
|
||
3 years ago
|
self.main.db_connection.save_category(data["name"])
|
||
4 years ago
|
|
||
|
def on_category_deletion_clicked(self):
|
||
|
dialog = CategoryModDialog(self, delete_category=True)
|
||
|
|
||
|
data = dialog.exec_()
|
||
|
if data is None:
|
||
|
return
|
||
|
|
||
3 years ago
|
self.main.db_connection.remove_category(data["name"])
|
||
4 years ago
|
|
||
|
def on_prev_unknown_image_clicked(self):
|
||
3 years ago
|
unknown_image_index = self.main.get_prev_unknown_image()
|
||
3 years ago
|
logging.info("Previous unknown image clicked!")
|
||
4 years ago
|
|
||
|
result = QtWidgets.QMessageBox.question(self, "Switch Image?",
|
||
|
"Do you really want to skip to image #{1} \"{0}\"?"
|
||
3 years ago
|
.format(self.main.all_images[unknown_image_index],
|
||
4 years ago
|
unknown_image_index))
|
||
|
if result == QtWidgets.QMessageBox.Yes:
|
||
3 years ago
|
self.main.curr_image_index = unknown_image_index
|
||
|
self.main.refresh_shown_image()
|
||
4 years ago
|
|
||
|
def on_next_unknown_image_clicked(self):
|
||
3 years ago
|
unknown_image_index = self.main.get_next_unknown_image()
|
||
3 years ago
|
logging.info("Next unknown image clicked!")
|
||
4 years ago
|
|
||
|
result = QtWidgets.QMessageBox.question(self, "Switch Image?",
|
||
|
"Do you really want to skip to image #{1} \"{0}\"?"
|
||
3 years ago
|
.format(self.main.all_images[unknown_image_index],
|
||
4 years ago
|
unknown_image_index))
|
||
|
if result == QtWidgets.QMessageBox.Yes:
|
||
3 years ago
|
self.main.curr_image_index = unknown_image_index
|
||
|
self.main.refresh_shown_image()
|
||
4 years ago
|
|
||
|
def on_image_id_spinbox_changed(self, v: int):
|
||
|
if self.__tmp_imageid_spinbox == v:
|
||
3 years ago
|
logging.info("SpinBox change detected!")
|
||
4 years ago
|
result = QtWidgets.QMessageBox.question(self, "Switch Image?",
|
||
|
"Do you really want to skip to image #{1} \"{0}\"?"
|
||
3 years ago
|
.format(self.main.all_images[v],
|
||
4 years ago
|
v))
|
||
|
if result == QtWidgets.QMessageBox.Yes:
|
||
3 years ago
|
self.main.curr_image_index = v
|
||
|
self.main.refresh_shown_image()
|
||
4 years ago
|
|
||
|
self.__tmp_imageid_spinbox: int = v
|
||
|
|
||
|
def on_delete_image_clicked(self):
|
||
3 years ago
|
logging.info("Delete clicked!")
|
||
3 years ago
|
art_hash = self.main.get_md5_of_image(self.curr_art_path)
|
||
|
if self.main.db_connection.get_art_by_hash(art_hash) is not None:
|
||
3 years ago
|
logging.debug("Delete on known image")
|
||
4 years ago
|
confirm_result = QtWidgets.QMessageBox.question(self, "Delete data?", "Do you really wish to delete all "
|
||
|
"data from the DB about this image?")
|
||
|
|
||
|
if confirm_result == QtWidgets.QMessageBox.Yes:
|
||
3 years ago
|
logging.info(f"deleting image data of \"{self.curr_image_title}\"")
|
||
3 years ago
|
self.main.db_connection.remove_image(art_hash)
|
||
4 years ago
|
else:
|
||
|
return
|
||
|
|
||
|
else:
|
||
3 years ago
|
logging.debug("Delete on unknown image")
|
||
4 years ago
|
confirm_result = QtWidgets.QMessageBox.question(self, "Delete image?", "Do you really wish to delete this "
|
||
|
"image?")
|
||
|
|
||
|
if confirm_result == QtWidgets.QMessageBox.Yes:
|
||
3 years ago
|
logging.info(f"deleting image file {self.curr_art_path}")
|
||
3 years ago
|
self.main.delete_image(self.curr_art_path)
|
||
4 years ago
|
else:
|
||
|
return
|
||
|
|
||
3 years ago
|
self.main.refresh_shown_image()
|
||
3 years ago
|
|
||
|
def on_link_label_activated(self, link: str):
|
||
3 years ago
|
logging.debug(f"Source link activated! {link}")
|
||
3 years ago
|
QDesktopServices.openUrl(QUrl(link))
|
||
|
|
||
|
def on_image_author_label_activated(self, link: str):
|
||
3 years ago
|
logging.debug(f"Image author link activated! {link}")
|
||
3 years ago
|
QDesktopServices.openUrl(QUrl(link))
|
||
|
|
||
3 years ago
|
def on_link_file_label_activated(self, link: str):
|
||
|
logging.debug(f"File label link activated! {link}")
|
||
|
QDesktopServices.openUrl(QUrl(link))
|
||
|
|
||
3 years ago
|
def on_description_change(self):
|
||
|
self.data_changed = True
|
||
3 years ago
|
|
||
|
def on_browser_clicked(self):
|
||
|
logging.debug("Clicked on open ArtNet browser!")
|
||
|
self.main.switch_to_browser()
|
||
3 years ago
|
|
||
|
def on_source_link_button_clicked(self):
|
||
|
logging.debug("Clicked on link button!")
|
||
|
dialog = LinkInputDialog(self, self.curr_link)
|
||
|
link = dialog.exec_()
|
||
|
|
||
|
if link is None: # dialog was cancelled
|
||
|
logging.debug("Cancelled link dialog.")
|
||
|
return
|
||
|
|
||
|
logging.info(f"Setting source link to \"{link}\"")
|
||
|
self.set_image_title_link(link)
|
||
|
|
||
|
def on_collection_button_clicked(self):
|
||
|
logging.debug("Clicked on collection button!")
|
||
|
QtWidgets.QMessageBox.information(self, "Not Implemented", "This feature has not been implemented yet!")
|
||
2 years ago
|
|
||
|
selected_collections = CollectionSelectDialog(self).exec_(self.curr_collections)
|
||
|
selected_collections = [self.get_collection(collection_id=c[0]) for c in selected_collections] # TODO verify that it returns the art-collection details if there are any
|
||
|
|
||
|
for selected_collection in selected_collections: # allow editing the collection rankings
|
||
|
result = CollectionModDialog(self, db_connection=self.main.db_connection, edit_collection=False)\
|
||
|
.exec_(selected_collection)
|
||
|
print()
|
||
|
|
||
|
self.set_current_collections(list(set(list(self.curr_collections + selected_collections))))
|
||
|
|
||
|
def on_custom_context_menu_requested(self):
|
||
|
action = QtWidgets.QAction('Copy URL to clipboard')
|
||
|
action.triggered.connect(self.on_copy_url_to_clipboard)
|
||
|
menu = QtWidgets.QMenu()
|
||
|
menu.addAction(action)
|
||
|
menu.exec_(QCursor.pos())
|
||
|
|
||
|
def on_copy_url_to_clipboard(self):
|
||
|
QtWidgets.QApplication.clipboard().setText(os.path.join(self.main.config.data['file_root'], self.curr_art_path))
|
||
|
|