|
|
|
import validators, os
|
|
|
|
import logging
|
|
|
|
|
|
|
|
from PyQt5 import QtWidgets
|
|
|
|
from PyQt5.QtCore import Qt, QSize, QUrl
|
|
|
|
from PyQt5.QtGui import QPixmap, QResizeEvent, QKeyEvent, QStandardItemModel, QStandardItem, QMovie, QDesktopServices
|
|
|
|
from PyQt5 import QtMultimedia
|
|
|
|
from PyQt5.QtMultimediaWidgets import QVideoWidget
|
|
|
|
|
|
|
|
from ArtNet.gui.windows.picture_importer import Ui_MainWindow
|
|
|
|
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
|
|
|
|
|
|
|
|
from ArtNet.web.link_generator import LinkGenerator
|
|
|
|
|
|
|
|
|
|
|
|
class Window(QtWidgets.QMainWindow):
|
|
|
|
|
|
|
|
UNKNOWN_TITLE = "-Title Unknown-"
|
|
|
|
UNKNOWN_PRESENCE = "(Not in Database)"
|
|
|
|
|
|
|
|
def __init__(self, main):
|
|
|
|
super(Window, self).__init__()
|
|
|
|
|
|
|
|
self.__main = main
|
|
|
|
|
|
|
|
self.__pixmap: QPixmap = None
|
|
|
|
self.__video: QVideoWidget = None
|
|
|
|
self.__player: QtMultimedia.QMediaPlayer = None
|
|
|
|
self.__text_player: QtWidgets.QTextEdit = None
|
|
|
|
self.__showing_video: bool = False
|
|
|
|
self.__showing_text: bool = False
|
|
|
|
|
|
|
|
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()
|
|
|
|
self.curr_imply_tags: list = list()
|
|
|
|
self.curr_tag_aliases: list = list()
|
|
|
|
|
|
|
|
self.setting_up_data: bool = True
|
|
|
|
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)
|
|
|
|
|
|
|
|
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)
|
|
|
|
self.ui.delete_button.clicked.connect(self.on_delete_image_clicked)
|
|
|
|
|
|
|
|
self.ui.link_label.linkActivated.connect(self.on_link_label_activated)
|
|
|
|
self.ui.image_author_label.linkActivated.connect(self.on_image_author_label_activated)
|
|
|
|
|
|
|
|
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)
|
|
|
|
self.ui.link_line.textChanged.connect(self.on_link_line_change)
|
|
|
|
self.ui.description_edit.textChanged.connect(self.on_description_change)
|
|
|
|
|
|
|
|
self.ui.link_label.setText("No Source Available")
|
|
|
|
self.ui.image_file_label.setTextInteractionFlags(Qt.TextSelectableByMouse)
|
|
|
|
self.set_image_title_link()
|
|
|
|
|
|
|
|
self.ui.description_edit.setReadOnly(False)
|
|
|
|
|
|
|
|
self.on_tag_search_change()
|
|
|
|
self.center()
|
|
|
|
|
|
|
|
@property
|
|
|
|
def data_changed(self):
|
|
|
|
return self.__data_changed
|
|
|
|
|
|
|
|
@data_changed.setter
|
|
|
|
def data_changed(self, v: bool):
|
|
|
|
self.__data_changed = v
|
|
|
|
|
|
|
|
if self.curr_image_title is None:
|
|
|
|
return
|
|
|
|
if " (Not in Database)" in self.curr_image_title and v and not self.setting_up_data:
|
|
|
|
self.curr_image_title = self.curr_image_title.replace(" (Not in Database)", "")
|
|
|
|
self.setting_up_data = True
|
|
|
|
self.ui.image_title_line.setText(self.curr_image_title)
|
|
|
|
self.setting_up_data = False
|
|
|
|
|
|
|
|
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()
|
|
|
|
self.move(int((screen.width() - size.width()) / 3), int((screen.height() - size.height()) / 5))
|
|
|
|
|
|
|
|
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:
|
|
|
|
"""
|
|
|
|
new_title = self.curr_image_title
|
|
|
|
new_description = self.ui.description_edit.toPlainText().strip()
|
|
|
|
if new_title == self.curr_file_name or len(new_title) == 0 or new_title == Window.UNKNOWN_TITLE:
|
|
|
|
new_title = None
|
|
|
|
if new_description is None or len(new_description) == 0:
|
|
|
|
new_description = None
|
|
|
|
else:
|
|
|
|
new_description = new_description.strip()
|
|
|
|
image_data = {
|
|
|
|
"ID": self.curr_art_id,
|
|
|
|
"title": new_title,
|
|
|
|
"authors": self.curr_presences,
|
|
|
|
"path": self.curr_art_path,
|
|
|
|
"tags": self.curr_tags,
|
|
|
|
"link": self.curr_link,
|
|
|
|
"md5_hash": self.__main.get_md5_of_image(self.curr_art_path),
|
|
|
|
"description": new_description,
|
|
|
|
}
|
|
|
|
|
|
|
|
for presence in self.curr_presences:
|
|
|
|
if presence[-1] == Window.UNKNOWN_PRESENCE:
|
|
|
|
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
|
|
|
|
|
|
|
|
self.__main.db_connection.save_image(ID=image_data["ID"], title=image_data["title"],
|
|
|
|
authors=image_data["authors"], path=image_data["path"],
|
|
|
|
tags=image_data["tags"], link=image_data["link"],
|
|
|
|
md5_hash=image_data["md5_hash"], desc=image_data["description"])
|
|
|
|
|
|
|
|
self.set_temporary_status_message("Saved {0} ({1}) to ArtNet DB!"
|
|
|
|
.format(image_data["title"], image_data["ID"]), 5000)
|
|
|
|
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
|
|
|
|
self.__main.db_connection.save_presence(name=name, domain=domain, artist_ID=artist[0], link=link)
|
|
|
|
|
|
|
|
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)
|
|
|
|
"""
|
|
|
|
return self.__main.db_connection.search_fuzzy_presence(presence_name, presence_domain, all_if_empty=True)
|
|
|
|
|
|
|
|
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:
|
|
|
|
"""
|
|
|
|
self.__main.db_connection.save_artist(ID, description)
|
|
|
|
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
|
|
|
|
|
|
|
|
return self.__main.db_connection.search_fuzzy_artists(ID_int, description)
|
|
|
|
|
|
|
|
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:
|
|
|
|
"""
|
|
|
|
return self.__main.db_connection.get_artist(id)
|
|
|
|
|
|
|
|
def remove_artist(self, id: int):
|
|
|
|
"""
|
|
|
|
Delte the given artist from the database.
|
|
|
|
:param id:
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
self.__main.db_connection.remove_artist(id)
|
|
|
|
|
|
|
|
def get_artist_presences(self, id: int) -> list:
|
|
|
|
"""
|
|
|
|
Query for all presences associated with the given artist.
|
|
|
|
:param id:
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
return self.__main.db_connection.get_artist_presences(id)
|
|
|
|
|
|
|
|
def get_all_artists(self) -> list:
|
|
|
|
"""
|
|
|
|
Queries the database for a list of all available arists (not presences).
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
return self.__main.db_connection.get_all_artists()
|
|
|
|
|
|
|
|
def get_presence(self, name: str, domain: str):
|
|
|
|
"""
|
|
|
|
Query a search for the presence fitting the data
|
|
|
|
:param name:
|
|
|
|
:param domain:
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
result = self.__main.db_connection.get_presence(name, domain)
|
|
|
|
|
|
|
|
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:
|
|
|
|
"""
|
|
|
|
self.__main.db_connection.remove_presence(name, domain)
|
|
|
|
|
|
|
|
def get_presences_art(self, name: str, domain: str):
|
|
|
|
"""
|
|
|
|
Query a list of art owned by the given presence
|
|
|
|
:param name:
|
|
|
|
:param domain:
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
return self.__main.db_connection.get_presences_art(name, domain)
|
|
|
|
|
|
|
|
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:
|
|
|
|
if domain == Window.UNKNOWN_PRESENCE:
|
|
|
|
presences.remove((name, domain))
|
|
|
|
elif len(presences) == 0:
|
|
|
|
presences = [(self.curr_art_path.split("/")[0], Window.UNKNOWN_PRESENCE)]
|
|
|
|
self.curr_presences = presences
|
|
|
|
|
|
|
|
if self.curr_presences is not None:
|
|
|
|
self.set_presence_label_text(self.curr_presences)
|
|
|
|
|
|
|
|
self.data_changed = True
|
|
|
|
|
|
|
|
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:
|
|
|
|
return self.__main.db_connection.get_all_categories()
|
|
|
|
|
|
|
|
return self.__main.db_connection.search_fuzzy_categories(search)
|
|
|
|
|
|
|
|
def get_image_link_from_line(self) -> str:
|
|
|
|
"""
|
|
|
|
Gets the image link from the QLineEdit if it is a valid link.
|
|
|
|
|
|
|
|
Otherwise an empty string
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
return self.ui.link_line.text()
|
|
|
|
|
|
|
|
def set_presence_label_text(self, presences: list):
|
|
|
|
"""
|
|
|
|
Set the label listing all current presences and include links if possible.
|
|
|
|
:param presences:
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
links = []
|
|
|
|
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)
|
|
|
|
|
|
|
|
def set_image_title_link(self) -> str:
|
|
|
|
"""
|
|
|
|
Sets the Image title to a link if there is link data given for this image.
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
self.ui.link_label.setText("No Source Available")
|
|
|
|
link = self.ui.link_line.text()
|
|
|
|
|
|
|
|
if validators.url(link):
|
|
|
|
self.curr_link = link
|
|
|
|
self.data_changed = True
|
|
|
|
hyperlink = "<a href=\"{0}\">{1}</a>".format(link, "Source")
|
|
|
|
self.ui.link_label.setText(hyperlink)
|
|
|
|
return link
|
|
|
|
elif len(link) == 0:
|
|
|
|
return ""
|
|
|
|
else:
|
|
|
|
self.ui.link_label.setText("No Source Available")
|
|
|
|
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:
|
|
|
|
"""
|
|
|
|
return self.__main.db_connection.get_tag_by_name(name)
|
|
|
|
|
|
|
|
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:
|
|
|
|
"""
|
|
|
|
return self.__main.db_connection.get_tag_aliases_by_name(name)
|
|
|
|
|
|
|
|
def get_tag_implications(self, name: str) -> list:
|
|
|
|
"""
|
|
|
|
Query a search for the tag's implications to the DB
|
|
|
|
:param name:
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
return self.__main.db_connection.get_tag_implications(name)
|
|
|
|
|
|
|
|
def get_tag_search_result(self, name: str) -> list:
|
|
|
|
"""
|
|
|
|
Query a search for tags to the DB that are like name
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
return self.__main.db_connection.search_fuzzy_tag(name, all_if_empty=True)
|
|
|
|
|
|
|
|
def set_tag_search_result_list(self, tags: list):
|
|
|
|
"""
|
|
|
|
Set the tags in the search result list to tags
|
|
|
|
:param tags:
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
item_model = QStandardItemModel(self.ui.search_result_list)
|
|
|
|
|
|
|
|
for tag in tags:
|
|
|
|
item = QStandardItem(tag)
|
|
|
|
flags = Qt.ItemIsEnabled
|
|
|
|
|
|
|
|
if tag not in self.curr_imply_tags+self.curr_tag_aliases and tag not in self.curr_tags:
|
|
|
|
# new tag and not implied yet
|
|
|
|
item.setData(Qt.Unchecked, Qt.CheckStateRole)
|
|
|
|
flags |= Qt.ItemIsUserCheckable
|
|
|
|
if self.curr_tags is not None and tag in (self.curr_tags+self.curr_imply_tags+self.curr_tag_aliases):
|
|
|
|
# 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)
|
|
|
|
|
|
|
|
def set_description_text(self, text: str):
|
|
|
|
"""
|
|
|
|
Set the text for the description field
|
|
|
|
"""
|
|
|
|
self.ui.description_edit.setText(text)
|
|
|
|
|
|
|
|
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:
|
|
|
|
:param no_implication: bool indicating if the implication list should also be updated
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
self.curr_tags = tags
|
|
|
|
item_model = QStandardItemModel(self.ui.tag_list)
|
|
|
|
|
|
|
|
for tag in tags:
|
|
|
|
item = QStandardItem(tag)
|
|
|
|
item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
|
|
|
|
item.setData(Qt.Unchecked, Qt.CheckStateRole)
|
|
|
|
|
|
|
|
item_model.appendRow(item)
|
|
|
|
|
|
|
|
if set_checked:
|
|
|
|
item.setCheckState(Qt.Checked)
|
|
|
|
|
|
|
|
item_model.itemChanged.connect(self.on_tag_item_changed)
|
|
|
|
self.ui.tag_list.setModel(item_model)
|
|
|
|
|
|
|
|
implied_tags = []
|
|
|
|
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)
|
|
|
|
|
|
|
|
self.set_implied_list(implied_tags)
|
|
|
|
self.curr_tag_aliases = list()
|
|
|
|
for tag in tags+implied_tags:
|
|
|
|
self.curr_tag_aliases += self.__main.db_connection.get_tag_aliases_by_name(tag)
|
|
|
|
|
|
|
|
self.data_changed = True
|
|
|
|
|
|
|
|
def set_implied_list(self, tags: list):
|
|
|
|
"""
|
|
|
|
Sets the implied tags in the imply list
|
|
|
|
:param tags:
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
self.curr_imply_tags = tags
|
|
|
|
item_model = QStandardItemModel(self.ui.implied_tag_list)
|
|
|
|
done = []
|
|
|
|
for tag in tags:
|
|
|
|
if tag in done:
|
|
|
|
continue
|
|
|
|
else:
|
|
|
|
done.append(tag)
|
|
|
|
item = QStandardItem(tag)
|
|
|
|
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,
|
|
|
|
link: str, file_name: str, description: str):
|
|
|
|
"""
|
|
|
|
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:
|
|
|
|
: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
|
|
|
|
self.set_current_presences(image_authors)
|
|
|
|
|
|
|
|
file_ending = relative_path.split(".")[-1]
|
|
|
|
if self.__showing_video: # remove old video from image layout
|
|
|
|
#self.ui.image_frame.layout().removeWidget(self.__video)
|
|
|
|
self.__video.hide()
|
|
|
|
self.ui.image_label.show()
|
|
|
|
if self.__showing_text: # remove text are from image layout
|
|
|
|
self.__text_player.hide()
|
|
|
|
self.ui.image_label.show()
|
|
|
|
|
|
|
|
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)
|
|
|
|
elif file_ending in ["webm", "mp4", "mov", "mkv"]:
|
|
|
|
self.__showing_video = True
|
|
|
|
if isinstance(self.__video, QVideoWidget):
|
|
|
|
self.__video.show()
|
|
|
|
else:
|
|
|
|
self.__video = QVideoWidget() if self.__video is None else self.__video
|
|
|
|
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)
|
|
|
|
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()
|
|
|
|
|
|
|
|
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()
|
|
|
|
self.ui.link_line.setText(link)
|
|
|
|
self.ui.image_file_label.setText(file_name)
|
|
|
|
self.ui.description_edit.setText(description)
|
|
|
|
self.set_image_title_link()
|
|
|
|
self.set_image_id_spinbox()
|
|
|
|
|
|
|
|
self.data_changed = False # reset any triggered change detection
|
|
|
|
|
|
|
|
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
|
|
|
|
+ f" ({round(self.__main.known_image_amount/len(self.__main.all_images), 5)}%)")
|
|
|
|
|
|
|
|
def set_image_id_spinbox(self):
|
|
|
|
"""
|
|
|
|
Sets the imageIDSpinBox to the image ID of the currently displayed image
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
self.ui.imageNumberSpinBox.setMinimum(0)
|
|
|
|
self.ui.imageNumberSpinBox.setMaximum(len(self.__main.all_images)-1)
|
|
|
|
self.ui.imageNumberSpinBox.setValue(self.__main.curr_image_index)
|
|
|
|
|
|
|
|
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
|
|
|
|
#self.__player.setScaledSize(size)
|
|
|
|
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:
|
|
|
|
super(Window, self).keyPressEvent(a0)
|
|
|
|
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:
|
|
|
|
logging.debug("Pressed Enter!")
|
|
|
|
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)
|
|
|
|
|
|
|
|
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)
|
|
|
|
item_data = self.ui.search_result_list.model().itemData(model_index)
|
|
|
|
|
|
|
|
if item_data[0] not in self.curr_tags: # add/remove new selected tag to the lists
|
|
|
|
self.curr_tags.append(item_data[0])
|
|
|
|
else:
|
|
|
|
self.curr_tags.remove(item_data[0])
|
|
|
|
|
|
|
|
self.set_tag_list(self.curr_tags) # update relevant lists
|
|
|
|
self.set_tag_search_result_list([item_data[0]])
|
|
|
|
logging.debug(item_data)
|
|
|
|
|
|
|
|
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):
|
|
|
|
logging.info("Clicked Save!")
|
|
|
|
self.set_image_title_link()
|
|
|
|
self.save_changes()
|
|
|
|
|
|
|
|
def on_import_tags_clicked(self):
|
|
|
|
logging.info("Clicked Import!")
|
|
|
|
dialog = TagImportDialog(self)
|
|
|
|
if len(self.get_image_link_from_line()) == 0 or self.get_image_link_from_line() == '(Unknown)':
|
|
|
|
url = LinkGenerator.get_instance().construct_link(self.curr_file_name,
|
|
|
|
LinkGenerator.get_instance()
|
|
|
|
.predict_domain(self.curr_file_name))
|
|
|
|
self.ui.link_line.setText(url) # Update no link to the predicted link
|
|
|
|
else:
|
|
|
|
url = self.get_image_link_from_line()
|
|
|
|
|
|
|
|
r = self.__main.scrape_tags(self.curr_file_name, url=url,
|
|
|
|
art_ID=self.curr_art_id)
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
self.ui.link_line.setText(url)
|
|
|
|
self.set_image_title_link()
|
|
|
|
tags, artists = r
|
|
|
|
|
|
|
|
i = 0
|
|
|
|
while i < len(tags): # workaround for an issue with altering lists during iteration
|
|
|
|
r = self.__main.db_connection.get_tag_by_name(tags[i])
|
|
|
|
if len(r) > 0:
|
|
|
|
self.curr_tags.append(tags[i])
|
|
|
|
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
|
|
|
|
|
|
|
|
self.__main.import_tags(result)
|
|
|
|
self.set_tag_list(self.curr_tags)
|
|
|
|
|
|
|
|
def on_next_clicked(self):
|
|
|
|
logging.info("Clicked Next!")
|
|
|
|
if not self.check_save_changes():
|
|
|
|
return
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
|
|
|
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):
|
|
|
|
logging.info("Clicked previous!")
|
|
|
|
if not self.check_save_changes():
|
|
|
|
return
|
|
|
|
|
|
|
|
self.__main.curr_image_index -= 1
|
|
|
|
if self.__main.curr_image_index < 0:
|
|
|
|
self.__main.curr_image_index += len(self.__main.all_images)
|
|
|
|
|
|
|
|
self.__main.refresh_shown_image()
|
|
|
|
|
|
|
|
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:
|
|
|
|
logging.info("Opened presence docker!")
|
|
|
|
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())
|
|
|
|
self.ui.presence_docker_button.setText(">")
|
|
|
|
self.presence_docker_open = True
|
|
|
|
else:
|
|
|
|
logging.info("Closed presence docker!")
|
|
|
|
self.presence_docker.setParent(None)
|
|
|
|
self.ui.presence_docker_button.setText("<")
|
|
|
|
|
|
|
|
self.presence_docker.destroy()
|
|
|
|
self.presence_docker = None
|
|
|
|
self.presence_docker_open = False
|
|
|
|
|
|
|
|
def on_artnet_root_change_clicked(self):
|
|
|
|
logging.info("Clicked changing ArtNet root!")
|
|
|
|
dialog = QtWidgets.QFileDialog(self, 'Choose new ArtNet root:')
|
|
|
|
dialog.setFileMode(QtWidgets.QFileDialog.Directory)
|
|
|
|
dialog.setOptions(QtWidgets.QFileDialog.ShowDirsOnly)
|
|
|
|
|
|
|
|
directory = dialog.getExistingDirectory()
|
|
|
|
self.__main.change_root(directory)
|
|
|
|
|
|
|
|
def on_db_connection_change_clicked(self):
|
|
|
|
logging.info("Clicked db connection change!")
|
|
|
|
dialog = DBDialog(self)
|
|
|
|
prev_db_data = self.__main.get_db_connection_details()
|
|
|
|
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
|
|
|
|
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"])
|
|
|
|
|
|
|
|
def on_tag_creation_clicked(self):
|
|
|
|
logging.info("Clicked Tag Creation!")
|
|
|
|
dialog = TagModifyDialog(self, create_tag=True)
|
|
|
|
|
|
|
|
tag_data: dict = dialog.exec_()
|
|
|
|
logging.debug("Got Tag data", tag_data)
|
|
|
|
if tag_data is None or len(tag_data.keys()) == 0: # got canceled?
|
|
|
|
return
|
|
|
|
|
|
|
|
if len(self.get_tag(tag_data["name"])) > 0:
|
|
|
|
QtWidgets.QMessageBox.information(self, "Duplicate Tag", "The tag \"{0}\" already exists in the db!"
|
|
|
|
.format(tag_data["name"]))
|
|
|
|
return
|
|
|
|
self.__main.db_connection.create_tag(name=tag_data["name"], description=tag_data["description"],
|
|
|
|
aliases=tag_data["aliases"], implications=tag_data["implications"],
|
|
|
|
category=tag_data["category"])
|
|
|
|
self.on_tag_search_change()
|
|
|
|
|
|
|
|
def on_tag_deletion_clicked(self):
|
|
|
|
logging.info("Clicked Tag Deletion!")
|
|
|
|
dialog = TagSelectDialog(self, delete_tag=True)
|
|
|
|
|
|
|
|
tag = dialog.exec_()
|
|
|
|
logging.debug("Got Tag", tag)
|
|
|
|
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
|
|
|
|
|
|
|
|
self.__main.db_connection.remove_tag_by_name(tag["name"])
|
|
|
|
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_()
|
|
|
|
logging.debug("Got Tag data", tag_data)
|
|
|
|
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
|
|
|
|
|
|
|
|
if len(self.get_tag(tag_data['name'])) > 0:
|
|
|
|
QtWidgets.QMessageBox.information(self, "Tag already exists",
|
|
|
|
"The Tag \"{0}\" you wanted to create already exists! Skipping...")
|
|
|
|
else:
|
|
|
|
self.__main.db_connection.create_tag(name=tag_data["name"], description=tag_data["description"],
|
|
|
|
aliases=tag_data["aliases"], implications=tag_data["implications"],
|
|
|
|
category=tag_data["category"])
|
|
|
|
self.on_tag_search_change()
|
|
|
|
return tag_data
|
|
|
|
|
|
|
|
def on_tag_edit_clicked(self):
|
|
|
|
logging.info("Clicked Tag Editing!")
|
|
|
|
select_dialog = TagSelectDialog(self, delete_tag=False)
|
|
|
|
|
|
|
|
tag = select_dialog.exec_()
|
|
|
|
if tag is None or len(tag) == 0:
|
|
|
|
return
|
|
|
|
|
|
|
|
tag['aliases'] = self.__main.db_connection.get_tag_aliases_by_name(tag["name"])
|
|
|
|
tag['implications'] = self.__main.db_connection.get_tag_implications(tag["name"])
|
|
|
|
|
|
|
|
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"]
|
|
|
|
edit_dialog.category_selection = self.__main.db_connection.get_category_by_ID(tag["category_id"])[1]
|
|
|
|
edit_dialog.set_all_categories()
|
|
|
|
|
|
|
|
tag_data = edit_dialog.exec_()
|
|
|
|
logging.debug("Got Tag data", tag_data)
|
|
|
|
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
|
|
|
|
|
|
|
|
self.__main.db_connection.edit_tag(tag_id=tag["ID"],
|
|
|
|
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])
|
|
|
|
self.on_tag_search_change()
|
|
|
|
|
|
|
|
def on_tag_search_item_changed(self, item: QStandardItem):
|
|
|
|
if item.checkState() == Qt.Checked:
|
|
|
|
self.curr_tags.append(item.text())
|
|
|
|
if item.checkState() == Qt.Unchecked:
|
|
|
|
if item.text() in self.curr_tags:
|
|
|
|
self.curr_tags.remove(item.text())
|
|
|
|
else:
|
|
|
|
return
|
|
|
|
self.set_tag_list(self.curr_tags)
|
|
|
|
|
|
|
|
def on_tag_item_changed(self, item: QStandardItem):
|
|
|
|
logging.debug("Item {0} has changed!".format(item.text()))
|
|
|
|
if item.checkState() == Qt.Unchecked:
|
|
|
|
if item.text() in self.curr_tags:
|
|
|
|
self.curr_tags.remove(item.text())
|
|
|
|
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):
|
|
|
|
tags = self.__main.db_connection.search_fuzzy_tag(self.ui.tag_search_bar.text(), all_if_empty=True)
|
|
|
|
|
|
|
|
result = []
|
|
|
|
for tag_name, tag_desc, tag_category in tags:
|
|
|
|
result.append(tag_name)
|
|
|
|
|
|
|
|
self.set_tag_search_result_list(result)
|
|
|
|
|
|
|
|
def on_category_creation_clicked(self):
|
|
|
|
dialog = CategoryModDialog(self, delete_category=False)
|
|
|
|
|
|
|
|
data = dialog.exec_()
|
|
|
|
if data is None:
|
|
|
|
return
|
|
|
|
|
|
|
|
self.__main.db_connection.save_category(data["name"])
|
|
|
|
|
|
|
|
def on_category_deletion_clicked(self):
|
|
|
|
dialog = CategoryModDialog(self, delete_category=True)
|
|
|
|
|
|
|
|
data = dialog.exec_()
|
|
|
|
if data is None:
|
|
|
|
return
|
|
|
|
|
|
|
|
self.__main.db_connection.remove_category(data["name"])
|
|
|
|
|
|
|
|
def on_link_line_change(self):
|
|
|
|
self.data_changed = True
|
|
|
|
|
|
|
|
def on_prev_unknown_image_clicked(self):
|
|
|
|
unknown_image_index = self.__main.get_prev_unknown_image()
|
|
|
|
logging.info("Previous unknown image clicked!")
|
|
|
|
|
|
|
|
result = QtWidgets.QMessageBox.question(self, "Switch Image?",
|
|
|
|
"Do you really want to skip to image #{1} \"{0}\"?"
|
|
|
|
.format(self.__main.all_images[unknown_image_index],
|
|
|
|
unknown_image_index))
|
|
|
|
if result == QtWidgets.QMessageBox.Yes:
|
|
|
|
self.__main.curr_image_index = unknown_image_index
|
|
|
|
self.__main.refresh_shown_image()
|
|
|
|
|
|
|
|
def on_next_unknown_image_clicked(self):
|
|
|
|
unknown_image_index = self.__main.get_next_unknown_image()
|
|
|
|
logging.info("Next unknown image clicked!")
|
|
|
|
|
|
|
|
result = QtWidgets.QMessageBox.question(self, "Switch Image?",
|
|
|
|
"Do you really want to skip to image #{1} \"{0}\"?"
|
|
|
|
.format(self.__main.all_images[unknown_image_index],
|
|
|
|
unknown_image_index))
|
|
|
|
if result == QtWidgets.QMessageBox.Yes:
|
|
|
|
self.__main.curr_image_index = unknown_image_index
|
|
|
|
self.__main.refresh_shown_image()
|
|
|
|
|
|
|
|
def on_image_id_spinbox_changed(self, v: int):
|
|
|
|
if self.__tmp_imageid_spinbox == v:
|
|
|
|
logging.info("SpinBox change detected!")
|
|
|
|
result = QtWidgets.QMessageBox.question(self, "Switch Image?",
|
|
|
|
"Do you really want to skip to image #{1} \"{0}\"?"
|
|
|
|
.format(self.__main.all_images[v],
|
|
|
|
v))
|
|
|
|
if result == QtWidgets.QMessageBox.Yes:
|
|
|
|
self.__main.curr_image_index = v
|
|
|
|
self.__main.refresh_shown_image()
|
|
|
|
|
|
|
|
self.__tmp_imageid_spinbox: int = v
|
|
|
|
|
|
|
|
def on_delete_image_clicked(self):
|
|
|
|
logging.info("Delete clicked!")
|
|
|
|
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:
|
|
|
|
logging.debug("Delete on known image")
|
|
|
|
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:
|
|
|
|
logging.info(f"deleting image data of \"{self.curr_image_title}\"")
|
|
|
|
self.__main.db_connection.remove_image(art_hash)
|
|
|
|
else:
|
|
|
|
return
|
|
|
|
|
|
|
|
else:
|
|
|
|
logging.debug("Delete on unknown image")
|
|
|
|
confirm_result = QtWidgets.QMessageBox.question(self, "Delete image?", "Do you really wish to delete this "
|
|
|
|
"image?")
|
|
|
|
|
|
|
|
if confirm_result == QtWidgets.QMessageBox.Yes:
|
|
|
|
logging.info(f"deleting image file {self.curr_art_path}")
|
|
|
|
self.__main.delete_image(self.curr_art_path)
|
|
|
|
else:
|
|
|
|
return
|
|
|
|
|
|
|
|
self.__main.refresh_shown_image()
|
|
|
|
|
|
|
|
def on_link_label_activated(self, link: str):
|
|
|
|
logging.debug("Source link activated!", link)
|
|
|
|
QDesktopServices.openUrl(QUrl(link))
|
|
|
|
|
|
|
|
def on_image_author_label_activated(self, link: str):
|
|
|
|
logging.debug("Image author link activated!", link)
|
|
|
|
QDesktopServices.openUrl(QUrl(link))
|
|
|
|
|
|
|
|
def on_description_change(self):
|
|
|
|
self.data_changed = True
|