parent
							
								
									a179ed4fb6
								
							
						
					
					
						commit
						99069ba77d
					
				| @ -0,0 +1,376 @@ | ||||
| import sys | ||||
| import os | ||||
| from hashlib import md5 | ||||
| 
 | ||||
| from PyQt5.QtWidgets import QApplication | ||||
| 
 | ||||
| from ArtNet.db.db_adapter import DBAdapter | ||||
| from ArtNet.file.file_reader import FileReader | ||||
| from ArtNet.file.config_reader import ConfigReader | ||||
| from ArtNet.gui.window import Window | ||||
| from ArtNet.gui.dialogs.db_connection_dialog.db_dialog import DBDialog | ||||
| from ArtNet.web.link_generator import LinkGenerator | ||||
| 
 | ||||
| 
 | ||||
| class ArtNetManager: | ||||
| 
 | ||||
|     def __init__(self, config_location: str = "."): | ||||
|         self.config = ConfigReader(config_location, "somePassword") | ||||
|         self.db_connection = None | ||||
|         self.__app = QApplication(sys.argv) | ||||
| 
 | ||||
|         while self.db_connection is None: | ||||
|             try: | ||||
|                 self.db_connection = self.create_db_connection(user=self.config.data["db"]["user"], | ||||
|                                                                password=self.config.data["db"]["password"], | ||||
|                                                                host=self.config.data["db"]["host"], | ||||
|                                                                port=self.config.data["db"]["port"], | ||||
|                                                                database=self.config.data["db"]["database"]) | ||||
|             except ValueError as e:  # db connection didn't work | ||||
|                 print(e) | ||||
|                 dialog = DBDialog() | ||||
|                 prev_db_data = self.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.change_db_connection(host=db_data["host"], port=db_data["port"], | ||||
|                                                  user=db_data["user"], password=db_data["password"], | ||||
|                                                  database=db_data["database"]) | ||||
|         self.window = Window(self) | ||||
| 
 | ||||
|         # TODO prompt for overwriting config when password invalid | ||||
|         # TODO sane default location | ||||
|         # TODO tag editor, don't kill dialog if fields are missing | ||||
|         # TODO display Tag Category when searching for tags | ||||
| 
 | ||||
|         # TODO remove string limit restrictions on implications & similar | ||||
| 
 | ||||
|         self.__file_root: str = None | ||||
|         self.window.on_artnet_root_change_clicked() | ||||
|         self.__file_reader = FileReader(self.__file_root) | ||||
| 
 | ||||
|         self.curr_image_index: int = None | ||||
| 
 | ||||
|         self.all_images: list = None | ||||
|         self.update_all_images_list() | ||||
| 
 | ||||
|         self.window.set_temporary_status_message("Hello o7", 5000) | ||||
| 
 | ||||
|     def run(self): | ||||
|         if len(self.all_images) > 0: | ||||
|             self.curr_image_index = 0 | ||||
|         self.change_image() | ||||
| 
 | ||||
|         self.window.show() | ||||
| 
 | ||||
|         sys.exit(self.__app.exec_()) | ||||
| 
 | ||||
|     def update_all_images_list(self): | ||||
|         """ | ||||
|         Updates the internal list of all images in the artnet root | ||||
|         :return: | ||||
|         """ | ||||
|         images = [] | ||||
|         artist_list = self.__file_reader.list_artists() | ||||
|         artist_list.sort() | ||||
|         for artist in artist_list: | ||||
|             for image in self.__file_reader.get_files(artist): | ||||
|                 images.append(image) | ||||
|         self.all_images = images | ||||
| 
 | ||||
|         self.known_image_amount = 0 | ||||
|         for image in self.all_images: | ||||
|             if self.db_connection.get_art_by_path(image) is not None: | ||||
|                 self.known_image_amount += 1 | ||||
| 
 | ||||
|     def scrape_tags(self, file_name: str, art_ID: int, url: str=None): | ||||
|         """ | ||||
|         Scrape the tags from the given url and return which one's are new | ||||
|         :param file_name: | ||||
|         :param art_ID: | ||||
|         :param url: | ||||
|         :return: | ||||
|         """ | ||||
|         if url is None: | ||||
|             return None | ||||
| 
 | ||||
|         #tags = ArtNet.web.Scrap_Tags.scrap_tags(file_name, url, ArtNet.web.link_generator.predict_domain(file_name)) | ||||
|         tags = LinkGenerator.get_instance().scrape_tags(url, LinkGenerator.get_instance().predict_domain(file_name)) | ||||
|         if tags is None: | ||||
|             return None | ||||
| 
 | ||||
|         already_applied_tags = self.db_connection.get_art_tags_by_ID(art_ID) | ||||
|         for i in range(len(already_applied_tags)): | ||||
|             already_applied_tags[i] = self.db_connection.get_tag_by_ID(already_applied_tags[i])[0][1].strip() | ||||
| 
 | ||||
|         importable_tags = [] | ||||
|         importable_artists = [] | ||||
|         scraped_tags = [] | ||||
|         for i in tags.values(): | ||||
|             scraped_tags += i | ||||
| 
 | ||||
|         for tag in scraped_tags: | ||||
|             result = self.db_connection.get_tag_by_name(tag) | ||||
|             if len(result) == 0:  # tag does not exist yet | ||||
|                 if tag in tags['artists']: | ||||
|                     importable_artists.append(tag) | ||||
|                     continue | ||||
|                 importable_tags.append(tag) | ||||
|                 continue | ||||
| 
 | ||||
|             if tag in already_applied_tags:  # tag is already applied | ||||
|                 continue | ||||
| 
 | ||||
|             result = self.db_connection.get_tag_impliers(tag) | ||||
|             if len(result) != 0:  # tag is implied by some other tag | ||||
|                 continue | ||||
| 
 | ||||
|             result = self.db_connection.get_tag_aliases(tag) | ||||
|             if len(result) != 0:  # tag is already tagged by an alias | ||||
|                 continue | ||||
| 
 | ||||
|             if tag in tags['artists']: | ||||
|                 importable_artists.append(tag) | ||||
|                 continue | ||||
|             importable_tags.append(tag)  # tag must be known and not tagged yet | ||||
| 
 | ||||
|         return importable_tags, importable_artists | ||||
| 
 | ||||
|     def import_tags(self, art_ID: int, tags): | ||||
|         """ | ||||
|         Add a list of given tags to the specified art_ID | ||||
|         :param art_ID: | ||||
|         :param tags: | ||||
|         :return: | ||||
|         """ | ||||
|         for tag in tags: | ||||
| 
 | ||||
|             if len(self.db_connection.get_tag_by_name(tag)) == 0:  # tag doesn't exist yet | ||||
|                 result = self.window.force_edit_tag_dialog(name=tag) | ||||
| 
 | ||||
|                 if result is None:  # tag creation was aborted | ||||
|                     continue | ||||
| 
 | ||||
|                 tag = result['name']  # overwrite with possibly new tag name | ||||
| 
 | ||||
|             tag = self.db_connection.get_tag_by_name(tag)[0][0] | ||||
|             self.window.curr_tags.append(tag) | ||||
|             self.window.data_changed = True | ||||
| 
 | ||||
|     def get_md5_of_image(self, path: str): | ||||
|         """ | ||||
|         Calculate the md5 hash of the image given by the path. | ||||
| 
 | ||||
|         :param path: assumed to be relative e.g. artist_A/image.jpg | ||||
|         :return: | ||||
|         """ | ||||
|         md5_hash = md5() | ||||
|         try: | ||||
|             with open(self.get_root() + os.path.sep + path, | ||||
|                       "rb") as file:  # read file part by part because of potential memory limits | ||||
|                 for chunk in iter(lambda: file.read(4096), b""): | ||||
|                     md5_hash.update(chunk) | ||||
| 
 | ||||
|             return md5_hash.hexdigest() | ||||
|         except FileNotFoundError: | ||||
|             self.update_all_images_list() | ||||
|             return None | ||||
| 
 | ||||
|     def delete_image(self, path: str, delete_instead_of_move: bool = False, trash_bin_folder_path: str = "trash"): | ||||
|         """ | ||||
|         Deletes the image from the root folder | ||||
|         :param path: | ||||
|         :param delete_instead_of_move: | ||||
|         :param trash_bin_folder_path: | ||||
|         :return: | ||||
|         """ | ||||
|         full_art_path = self.get_root() + os.path.sep + path | ||||
| 
 | ||||
|         if delete_instead_of_move: | ||||
|             print(f"Deleting the actual file {full_art_path} is disabled for now") | ||||
|             #os.remove(full_art_path) | ||||
|             #return | ||||
| 
 | ||||
|         trash_dst = trash_bin_folder_path + os.path.sep + path | ||||
|         t_splits = trash_dst.split(os.path.sep) | ||||
|         t_path = "" | ||||
|         for i in range(len(t_splits)-1): | ||||
|             t_path += os.path.sep + t_splits[i] | ||||
|         t_path = t_path[1:] | ||||
|         if not os.path.exists(t_path): | ||||
|             print(f"{t_path} did not exist and will be created!") | ||||
|             os.makedirs("."+os.path.sep+t_path) | ||||
|         os.rename(full_art_path, trash_dst) | ||||
|         print(f"Moving image {full_art_path} to {trash_dst}") | ||||
| 
 | ||||
|         self.update_all_images_list() | ||||
|         while self.curr_image_index >= len(self.all_images): | ||||
|             self.curr_image_index -= 1 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     @DeprecationWarning | ||||
|     def recalculate_hash_for_known_images(self): | ||||
|         """ | ||||
|         Calculate and write the md5 hash for every image known to the database. | ||||
| 
 | ||||
|         Important: For migration purposes only | ||||
|         :return: | ||||
|         """ | ||||
|         for i in range(len(self.all_images)): | ||||
|             image_db_result = self.db_connection.get_art_by_path(self.all_images[i]) | ||||
| 
 | ||||
|             if image_db_result is None:  # unknown image, skip | ||||
|                 continue | ||||
| 
 | ||||
|             hash_digest = self.get_md5_of_image(self.all_images[i]) | ||||
| 
 | ||||
|             authors = self.db_connection.get_authors_of_art(path=image_db_result["path"]) | ||||
|             tag_ids = self.db_connection.get_art_tags_by_ID(art_ID=image_db_result["ID"]) | ||||
|             tags = [] | ||||
|             for tag_id in tag_ids: | ||||
|                 tags.append(self.db_connection.get_tag_by_ID(tag_id)[0][1].strip()) | ||||
| 
 | ||||
|             self.db_connection.save_image(ID=image_db_result["ID"], path=image_db_result["path"], | ||||
|                                           title=image_db_result["title"], | ||||
|                                           link=image_db_result["link"], authors=authors, md5_hash=hash_digest, | ||||
|                                           tags=tags) | ||||
| 
 | ||||
|     def get_next_unknown_image(self) -> int: | ||||
|         """ | ||||
|         Search all images for the next image not known to the database in ascending order. | ||||
| 
 | ||||
|         :return: index of the next unknown image, None if there is no unknown image | ||||
|         """ | ||||
|         next_unknown: int = None | ||||
|         curr_searched_image_index = self.curr_image_index + 1 | ||||
| 
 | ||||
|         finished_loop = False | ||||
|         while next_unknown is None and not finished_loop: | ||||
|             if curr_searched_image_index >= len(self.all_images):  # wrap around to the start | ||||
|                 curr_searched_image_index = 0 | ||||
|             if curr_searched_image_index == self.curr_image_index:  # searched all images, nothing unknown | ||||
|                 break | ||||
| 
 | ||||
|             image_db_result = self.db_connection.get_art_by_path(self.all_images[curr_searched_image_index]) | ||||
|             if image_db_result is None: | ||||
|                 image_db_result = self.db_connection.get_art_by_hash( | ||||
|                     self.get_md5_of_image(self.all_images[curr_searched_image_index]) | ||||
|                 ) | ||||
| 
 | ||||
|             if image_db_result is None:  # image is unknown to database | ||||
|                 next_unknown = curr_searched_image_index | ||||
|                 break | ||||
|             else: | ||||
|                 curr_searched_image_index += 1 | ||||
| 
 | ||||
|         if next_unknown: | ||||
|             return curr_searched_image_index | ||||
|         else: | ||||
|             return None | ||||
| 
 | ||||
|     def get_prev_unknown_image(self) -> int: | ||||
|         """ | ||||
|         Search all images for the next image not known to the databse in descending order. | ||||
| 
 | ||||
|         :return:index of the next unknown image, None if there is no unknown image | ||||
|         """ | ||||
|         next_unknown: int = None | ||||
|         curr_searched_image_index = self.curr_image_index - 1 | ||||
| 
 | ||||
|         finished_loop = False | ||||
|         while next_unknown is None and not finished_loop: | ||||
|             if curr_searched_image_index <= 0:  # wrap around to the end | ||||
|                 curr_searched_image_index = len(self.all_images) - 1 | ||||
|             if curr_searched_image_index == self.curr_image_index:  # searched all images, nothing unknown | ||||
|                 break | ||||
| 
 | ||||
|             image_db_result = self.db_connection.get_art_by_path(self.all_images[curr_searched_image_index]) | ||||
| 
 | ||||
|             if image_db_result is None:  # image is unknown to database | ||||
|                 next_unknown = curr_searched_image_index | ||||
|                 break | ||||
|             else: | ||||
|                 curr_searched_image_index -= 1 | ||||
| 
 | ||||
|         if next_unknown: | ||||
|             return curr_searched_image_index | ||||
|         else: | ||||
|             return None | ||||
| 
 | ||||
|     def change_image(self): | ||||
|         """ | ||||
|         Refresh the image display to show the most current data according to the selected image | ||||
|         :return: | ||||
|         """ | ||||
|         self.window.setting_up_data = True | ||||
| 
 | ||||
|         image_db_result = self.db_connection.get_art_by_hash( | ||||
|             self.get_md5_of_image(self.all_images[self.curr_image_index]) | ||||
|         ) | ||||
|         #image_db_result = self.db_connection.get_art_by_path(self.all_images[self.curr_image_index]) | ||||
|         s = self.all_images[self.curr_image_index].split(os.path.sep) | ||||
|         if image_db_result is not None: | ||||
|             image_title = image_db_result["title"] if len(image_db_result["title"]) > 0 else "-Title Unknown-" | ||||
|             image_author = self.db_connection.get_authors_of_art_by_ID(image_db_result["ID"]) | ||||
|             image_link = image_db_result["link"] | ||||
|             art_ID = image_db_result["ID"] | ||||
|             if image_author is None or len(image_author) == 0: | ||||
|                 image_author = [(self.all_images[self.curr_image_index].split(os.path.sep)[0], "(Not in Database)")] | ||||
|         else: | ||||
|             art_ID = None | ||||
|             image_author = [(s[0], "(Not in Database)")] | ||||
|             image_title = s[-1] + " (Not in Database)" | ||||
|             image_link = "(Unknown)" | ||||
| 
 | ||||
|         print("Displaying", self.all_images[self.curr_image_index]) | ||||
|         self.window.display_image(image_title, image_author, | ||||
|                                   os.path.join(self.__file_root, self.all_images[self.curr_image_index]), | ||||
|                                   self.all_images[self.curr_image_index], | ||||
|                                   art_ID, image_link, file_name=s[-1]) | ||||
|         self.window.set_tag_list([self.db_connection.get_tag_by_ID(x)[0][1].strip() for x in self.db_connection.get_art_tags_by_ID(art_ID)]) | ||||
| 
 | ||||
|         self.window.data_changed = False | ||||
|         self.window.setting_up_data = False | ||||
| 
 | ||||
|     def create_db_connection(self, host: str, port: int, database: str, user: str, password: str) -> DBAdapter: | ||||
|         print("Changing db connection to ".format(host, port, user, password)) | ||||
|         return DBAdapter(user=user, password=password, host=host, port=port, database=database) | ||||
| 
 | ||||
|     def get_root(self) -> str: | ||||
|         return self.__file_root | ||||
| 
 | ||||
|     def change_root(self, path: str): | ||||
|         """ | ||||
|         Change the Artnet root path and confirm it is working. | ||||
| 
 | ||||
|         Rewrite config there | ||||
|         :param path: | ||||
|         :return: | ||||
|         """ | ||||
|         if len(path) == 0: | ||||
|             exit(0) | ||||
|         print("Changing root to", path) | ||||
|         self.__file_root = path | ||||
| 
 | ||||
|     def get_db_connection_details(self) -> dict: | ||||
|         return self.config.data["db"] | ||||
| 
 | ||||
|     def change_db_connection(self, host: str, port: int, database: str, user: str, password: str): | ||||
|         self.config.data["db"]["user"] = user | ||||
|         self.config.data["db"]["password"] = password | ||||
|         self.config.data["db"]["host"] = host | ||||
|         self.config.data["db"]["database"] = database | ||||
|         self.config.data["db"]["port"] = str(port) | ||||
| 
 | ||||
|         self.config.update_config() | ||||
| 
 | ||||
|         self.db_connection = self.create_db_connection(host, port, database, user, password) | ||||
| 
 | ||||
| 
 | ||||
| @ -0,0 +1,853 @@ | ||||
| import psycopg2 | ||||
| from psycopg2.errors import * | ||||
| from psycopg2.errorcodes import UNIQUE_VIOLATION | ||||
| 
 | ||||
| 
 | ||||
| class DBAdapter: | ||||
| 
 | ||||
|     def __init__(self, user: str, password: str, host: str = "localhost", port: int = 5432, database: str = "artnet"): | ||||
|         self.db = None | ||||
|         self.db_cursor = None | ||||
|         try: | ||||
|             self.db = psycopg2.connect(host=host, port=port, database=database, user=user, password=password) | ||||
|             self.db_cursor = self.db.cursor() | ||||
| 
 | ||||
|         except psycopg2.OperationalError as e: | ||||
|             raise ValueError("Invalid DB credentials!") | ||||
|         print("DB connection to {0}:{1}/{2}".format(host, port, database)) | ||||
| 
 | ||||
|     def save_image(self, ID: str, title: str, authors: list, path: str, tags: list, link: str, md5_hash: str): | ||||
|         """ | ||||
|         Updates or saves the given image data to the DB | ||||
|         :param ID: | ||||
|         :param title: | ||||
|         :param authors: | ||||
|         :param path: | ||||
|         :param tags: | ||||
|         :param link: | ||||
|         :param md5_hash: md5 hash as a hex digest | ||||
|         :return: | ||||
|         """ | ||||
|         print("Saving Image {0}:{1} authors: {2} path: {3} tags: {4} link: {5} hash:{6}" | ||||
|               .format(ID, title, authors, path, tags, link, md5_hash)) | ||||
|         d = {"title": title, "path": path, "id": ID, "link": link, "hash": md5_hash} | ||||
|         if self.get_art_by_path(path) is None: | ||||
|             if ID is None: | ||||
|                 self.db_cursor.execute( | ||||
|                     "INSERT INTO art (path, title, link, md5_hash) VALUES (%(path)s, %(title)s, %(link)s, %(hash)s)", | ||||
|                     d) | ||||
|                 ID = self.get_art_by_path(path)["ID"] | ||||
|                 d["id"] = ID | ||||
|             elif self.get_tag_by_ID(ID) is None: | ||||
|                 self.db_cursor.execute( | ||||
|                     "INSERT INTO art (path, title, link, md5_hash) VALUES (%(path)s, %(title)s, %(link)s, %(hash)s)", d) | ||||
|         else: | ||||
|             self.db_cursor.execute("UPDATE art SET path = %(path)s, title = %(title)s, link = %(link)s, " + | ||||
|                                    "md5_hash = %(hash)s WHERE id = %(id)s", d) | ||||
| 
 | ||||
|         if ID is None: | ||||
|             ID = self.get_art_by_path(path)["ID"] | ||||
|             assert(ID != None) | ||||
| 
 | ||||
|         old_tags = [self.get_tag_by_ID(x)[0][1].strip() for x in self.get_art_tags_by_ID(ID)] | ||||
|         for tag in tags: | ||||
|             if tag in old_tags:  # already present | ||||
|                 continue | ||||
|             d = {"id": ID, "tag": self.get_tag_ID(tag)} | ||||
|             try: | ||||
|                 self.db_cursor.execute("INSERT INTO art_tag (art_id, tag_ID) VALUES (%(id)s, %(tag)s)", d) | ||||
|             except psycopg2.Error as e: | ||||
|                 if e.pgcode == UNIQUE_VIOLATION: | ||||
|                     print(e) | ||||
|                     print("Skipping Unique Violation ...") | ||||
|                 else: | ||||
|                     raise e | ||||
| 
 | ||||
|         for old_tag in old_tags: | ||||
|             if old_tag not in tags:  # need to remove old tag | ||||
|                 self.remove_tag_from_image(art_ID=ID, tag_ID=self.get_tag_ID(old_tag)) | ||||
| 
 | ||||
|         old_authors = self.get_authors_of_art_by_ID(ID) | ||||
|         for author in authors: | ||||
|             if author in old_authors: | ||||
|                 continue | ||||
|             d = {"id": ID, "author_name": author[0], "author_domain": author[1]} | ||||
|             self.db_cursor.execute("INSERT INTO art_author (presence_name, presence_domain, art_ID) " + | ||||
|                                    "VALUES (%(author_name)s, %(author_domain)s, %(id)s)", d) | ||||
|         for old_author_name, old_author_domain in old_authors: | ||||
|             if (old_author_name, old_author_domain) not in authors:  # need to remove old author | ||||
|                 self.remove_artist_from_image(art_ID=ID, presence_name=old_author_name, | ||||
|                                               presence_domain=old_author_domain) | ||||
| 
 | ||||
|         self.db.commit() | ||||
| 
 | ||||
|     def remove_image(self, hash: str): | ||||
|         """ | ||||
|         Removes an image completely from the database. | ||||
| 
 | ||||
|         Does not affect tags or artists but removes art-tag, art-artist relations | ||||
|         :param hash: md5 hash of the image to be deleted | ||||
|         :return: | ||||
|         """ | ||||
|         image_data = self.get_art_by_hash(hash) | ||||
|         art_ID = image_data["ID"] | ||||
|         print(f"Deleting image #{art_ID} {image_data['title']}") | ||||
|         tags = self.get_art_tags_by_ID(art_ID) | ||||
|         for tag_ID in tags: | ||||
|             pass | ||||
|             #self.remove_tag_from_image(art_ID=art_ID, tag_ID=tag_ID) | ||||
|         authors = self.get_authors_of_art_by_ID(art_ID) | ||||
|         for presence_name, presence_domain in authors: | ||||
|             pass | ||||
|             #self.remove_artist_from_image(art_ID=art_ID, presence_name=presence_name, presence_domain=presence_domain) | ||||
| 
 | ||||
|         #self.db_cursor.execute("DELETE FROM art WHERE md5_hash = %(hash)s", {"hash": hash}) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     def remove_artist_from_image(self, art_ID, presence_name, presence_domain): | ||||
|         """ | ||||
|         Removes the relationship between an artist and the image | ||||
|         :param art_ID: | ||||
|         :param presence_name: | ||||
|         :param presence_domain: | ||||
|         :return: | ||||
|         """ | ||||
|         d = {"name": presence_name, "domain": presence_domain, "id": art_ID} | ||||
|         self.db_cursor.execute("DELETE FROM art_author " + | ||||
|                                "WHERE presence_name = %(name)s and " + | ||||
|                                "presence_domain = %(domain)s and art_ID = %(id)s", | ||||
|                                d) | ||||
| 
 | ||||
|     def remove_tag_from_image(self, art_ID, tag_ID): | ||||
|         """ | ||||
|         Remove the relationship between a tag and the image | ||||
|         :param art_ID: | ||||
|         :param tag_ID: | ||||
|         :return: | ||||
|         """ | ||||
|         d = {"id": art_ID, "tag": tag_ID} | ||||
|         self.db_cursor.execute("DELETE FROM art_tag WHERE art_id = %(id)s and tag_id = %(tag)s", d) | ||||
| 
 | ||||
|     def save_presence(self, name: str, domain: str, artist_ID: int, link: str): | ||||
|         """ | ||||
|         Save the presence entry with the given data. | ||||
| 
 | ||||
|         If it doesn't exist a new one will be created. | ||||
|         If the artist ID is unknown an exception will be thrown | ||||
|         :param name: | ||||
|         :param domain: | ||||
|         :param artist_ID: | ||||
|         :param link: | ||||
|         :return: | ||||
|         """ | ||||
|         print("Saving Presence {0}:{1} Artist: {2} Link: {3}".format(name, domain, artist_ID, link)) | ||||
|         artist = self.get_artist(artist_ID) | ||||
|         if artist is None or len(artist) == 0: | ||||
|             raise Exception("Unknown Artist to create/update Presence with!") | ||||
| 
 | ||||
|         presence = self.get_presence(name, domain) | ||||
|         d = {"name": name, "domain": domain, "artist": artist_ID, "link": link} | ||||
|         if len(presence) == 0:  # presence does not exist yet | ||||
|             self.db_cursor.execute("INSERT INTO presence (name, domain, artist, link) " + | ||||
|                                    "VALUES (%(name)s, %(domain)s, %(artist)s, %(link)s)", d) | ||||
|         else:  # presence exists, update it | ||||
|             self.db_cursor.execute("UPDATE presence SET artist = %(artist)s, link = %(link)s" + | ||||
|                                    "WHERE name = %(name)s and domain = %(domain)s", d) | ||||
| 
 | ||||
|         self.db.commit() | ||||
| 
 | ||||
|     def remove_presence(self, name: str, domain: str): | ||||
|         """ | ||||
|         Remove a presence from the database | ||||
|         :return: | ||||
|         """ | ||||
|         print("Removing Presence {0}:{1}".format(name, domain)) | ||||
|         d = {"name": name, "domain": domain} | ||||
|         self.db_cursor.execute("DELETE FROM presence WHERE name = %(name)s and domain = %(domain)s", d) | ||||
|         self.db.commit() | ||||
| 
 | ||||
|     def get_presence(self, name: str, domain: str): | ||||
|         """ | ||||
|         Queries the database for the presence and returns the result | ||||
| 
 | ||||
|         :param name: | ||||
|         :param domain: | ||||
|         :return: | ||||
|         """ | ||||
|         d = {"name": name, "domain": domain} | ||||
|         self.db_cursor.execute("SELECT name, domain, artist, link FROM presence " + | ||||
|                                "WHERE name = %(name)s and domain = %(domain)s", | ||||
|                                d) | ||||
|         rows = self.db_cursor.fetchall() | ||||
|         new_rows = [] | ||||
|         for row in rows: | ||||
|             new_rows.append((row[0].strip(), row[1].strip(), row[2], row[3])) | ||||
| 
 | ||||
|         return new_rows | ||||
| 
 | ||||
|     def save_artist(self, ID: int, description: str): | ||||
|         """ | ||||
|         Save (or update if ID is already taken) an artist to the DB | ||||
|         :param ID: | ||||
|         :param description: | ||||
|         :return: | ||||
|         """ | ||||
|         print("Saving artist {0}:{1}".format(ID, description)) | ||||
|         d = {"id": ID, "description": description} | ||||
|         if ID is None:  # no ID given, auto generate it | ||||
|             self.db_cursor.execute("INSERT INTO artist (description) VALUES (%(description)s)", d) | ||||
|         elif len(self.get_artist(ID)) != 0:  # artist exists already: | ||||
|             self.db_cursor.execute("UPDATE artist SET description = %(description)s WHERE id = %(id)s", d) | ||||
|         else:  # artist needs to be created | ||||
|             self.db_cursor.execute("INSERT INTO artist (id, description) VALUES (%(id)s, %(description)s)", d) | ||||
|         self.db.commit() | ||||
| 
 | ||||
|     def remove_artist(self, ID: int): | ||||
|         """ | ||||
|         Deletes the artist from the DB | ||||
|         :param ID: | ||||
|         :return: | ||||
|         """ | ||||
|         print("Deleting artist {0}".format(ID)) | ||||
|         d = {"id": ID} | ||||
|         self.db_cursor.execute("DELETE FROM Artist WHERE ID = %(id)s", d) | ||||
|         self.db.commit() | ||||
| 
 | ||||
|     def save_category(self, name: str): | ||||
|         """ | ||||
|         Save the category to the DB. | ||||
| 
 | ||||
|         Does not check if the category already exists | ||||
|         :param name: | ||||
|         :return: | ||||
|         """ | ||||
|         print("Saving category {0}!".format(name)) | ||||
|         d = {"name": name} | ||||
|         self.db_cursor.execute("INSERT INTO tag_category (name) VALUES (%(name)s)", d) | ||||
| 
 | ||||
|         self.db.commit() | ||||
| 
 | ||||
|     def remove_category(self, name: str): | ||||
|         """ | ||||
|         Remove the category from the DB. | ||||
|         :param name: | ||||
|         :return: | ||||
|         """ | ||||
|         d = {"name": name} | ||||
|         self.db_cursor.execute("DELETE FROM tag_category WHERE name = %(name)s", d) | ||||
| 
 | ||||
|         self.db.commit() | ||||
| 
 | ||||
|     def get_artist(self, ID: int): | ||||
|         """ | ||||
|         Queries the database for the artist (not presence) and returns the result | ||||
|         :param ID: | ||||
|         :return: | ||||
|         """ | ||||
|         d = {"id": ID} | ||||
|         self.db_cursor.execute("SELECT id, description FROM artist WHERE id = %(id)s", d) | ||||
| 
 | ||||
|         return self.db_cursor.fetchall() | ||||
| 
 | ||||
|     def get_artist_presences(self, id: int): | ||||
|         """ | ||||
|         Queries the databse for all presences associated with the artist and returns the result | ||||
|         :param id: | ||||
|         :return: | ||||
|         """ | ||||
|         d = {"id": id} | ||||
|         self.db_cursor.execute("SELECT name, domain, artist FROM Presence WHERE artist = %(id)s", d) | ||||
| 
 | ||||
|         return self.db_cursor.fetchall() | ||||
| 
 | ||||
|     def get_all_artists(self): | ||||
|         """ | ||||
|         Lists all available artists (not presences) and returns the result | ||||
|         :return: | ||||
|         """ | ||||
|         self.db_cursor.execute("SELECT id, description FROM artist") | ||||
| 
 | ||||
|     def get_art_by_hash(self, file_hash: str) -> dict: | ||||
|         """ | ||||
|         Queries the database for the art via its hash and returns it if available. | ||||
| 
 | ||||
|         Currently uses an md5 hash | ||||
|         :param file_hash: | ||||
|         :return: | ||||
|         """ | ||||
|         d = {"hash": file_hash} | ||||
|         self.db_cursor.execute("SELECT id, path, title, link, md5_hash FROM art where md5_hash = %(hash)s", d) | ||||
| 
 | ||||
|         result = self.db_cursor.fetchall() | ||||
| 
 | ||||
|         if len(result) == 0: | ||||
|             return None | ||||
|         else: | ||||
|             result = result[0] | ||||
|             image_data = { | ||||
|                 "ID": result[0], | ||||
|                 "path": result[1], | ||||
|                 "title": result[2], | ||||
|                 "link": result[3], | ||||
|                 "md5_hash": result[4], | ||||
|             } | ||||
|             return image_data | ||||
| 
 | ||||
|     def get_art_by_path(self, path: str) -> dict: | ||||
|         """ | ||||
|         Queries the database for the art via its path and returns it if available. | ||||
| 
 | ||||
|         Otherwise None | ||||
|         :param path: | ||||
|         :return: | ||||
|         """ | ||||
|         d = {"path": path} | ||||
|         self.db_cursor.execute("SELECT id, path, title, link, md5_hash FROM art where path = %(path)s", d) | ||||
| 
 | ||||
|         result = self.db_cursor.fetchall() | ||||
| 
 | ||||
|         if len(result) == 0: | ||||
|             return None | ||||
|         else: | ||||
|             result = result[0] | ||||
|             image_data = { | ||||
|                 "ID": result[0], | ||||
|                 "path": result[1], | ||||
|                 "title": result[2], | ||||
|                 "link": result[3], | ||||
|                 "md5_hash": result[4], | ||||
|             } | ||||
|             return image_data | ||||
| 
 | ||||
|     @DeprecationWarning | ||||
|     def get_authors_of_art(self, path: str): | ||||
|         """ | ||||
|         Get the authors (presence and author) of the given art. | ||||
|         :param path: | ||||
|         :return: | ||||
|         """ | ||||
|         art_data = self.get_art_by_path(path) | ||||
|         art_id: int = None | ||||
|         if art_data is not None: | ||||
|             art_id = art_data["ID"] | ||||
|         if art_id is None: | ||||
|             return None | ||||
|         d = {"id": art_id} | ||||
| 
 | ||||
|         self.db_cursor.execute("SELECT presence_name, presence_domain FROM art_author WHERE art_ID = %(id)s", d) | ||||
|         presences = self.db_cursor.fetchall() | ||||
| 
 | ||||
|         result = [] | ||||
|         for name, domain in presences: | ||||
|             result.append((name.strip(), domain.strip())) | ||||
| 
 | ||||
|         return result | ||||
| 
 | ||||
|     def get_authors_of_art_by_ID(self, id: int): | ||||
|         """ | ||||
|         Get the authors (presence and author) of the given art. | ||||
|         :param id: | ||||
|         :return: | ||||
|         """ | ||||
|         d = {"id": id} | ||||
| 
 | ||||
|         self.db_cursor.execute("SELECT presence_name, presence_domain FROM art_author WHERE art_ID = %(id)s", d) | ||||
|         presences = self.db_cursor.fetchall() | ||||
| 
 | ||||
|         result = [] | ||||
|         for name, domain in presences: | ||||
|             result.append((name.strip(), domain.strip())) | ||||
| 
 | ||||
|         return result | ||||
| 
 | ||||
|     def get_art_tags_by_ID(self, art_ID: int): | ||||
|         """ | ||||
|         Query the database for all tags associated with a given art ID | ||||
|         :param art_ID: | ||||
|         :return: | ||||
|         """ | ||||
|         d = {"id": art_ID} | ||||
|         self.db_cursor.execute("SELECT tag_ID FROM art_tag WHERE art_id = %(id)s", d) | ||||
| 
 | ||||
|         rows = self.db_cursor.fetchall() | ||||
|         new_rows = [] | ||||
|         for row in rows: | ||||
|             new_rows.append(row[0]) | ||||
|         return new_rows | ||||
| 
 | ||||
|     def search_fuzzy_presence(self, name: str, domain: str, all_if_empty: bool = False): | ||||
|         """ | ||||
|         Search a list of presences fitting the (name, domain) fuzzy. | ||||
|         :param name: | ||||
|         :param domain: | ||||
|         :param all_if_empty: | ||||
|         :return: | ||||
|         """ | ||||
|         if all_if_empty and (name is None or len(name) == 0) and (domain is None or len(domain) == 0): | ||||
|             self.db_cursor.execute("SELECT name, domain, artist FROM presence") | ||||
|         else: | ||||
|             self.db_cursor.execute("SELECT name, domain, artist FROM presence WHERE LOWER(name) LIKE LOWER('%{0}%')".format(name) + | ||||
|                                    " AND domain LIKE '%{0}%'".format(domain)) | ||||
|         rows = self.db_cursor.fetchall() | ||||
| 
 | ||||
|         result = [] | ||||
|         for row in rows: | ||||
|             result.append((row[0].strip(), row[1].strip())) | ||||
|         return result | ||||
| 
 | ||||
|     def get_presences_art(self, name, domain): | ||||
|         """ | ||||
|         Query a list of all art that includes the given presence as their author | ||||
|         :param name: | ||||
|         :param domain: | ||||
|         :return: | ||||
|         """ | ||||
|         d = {"name": name, "domain": domain} | ||||
|         self.db_cursor.execute( | ||||
|             "SELECT art_ID FROM Art_Author WHERE presence_name = %(name)s and presence_domain = %(domain)s", d) | ||||
| 
 | ||||
|         return self.db_cursor.fetchall() | ||||
| 
 | ||||
|     def get_all_categories(self) -> list: | ||||
|         """ | ||||
|         Return all categories in the database. | ||||
|         :return: | ||||
|         """ | ||||
|         self.db_cursor.execute("SELECT name FROM tag_category") | ||||
| 
 | ||||
|         rows = [] | ||||
|         for row in self.db_cursor.fetchall(): | ||||
|             rows.append(row[0].strip()) | ||||
| 
 | ||||
|         return rows | ||||
| 
 | ||||
|     def search_fuzzy_categories(self, search: str) -> list: | ||||
|         """ | ||||
|         Search a list of categories fitting search fuzzy. | ||||
|         :param search: | ||||
|         :return: | ||||
|         """ | ||||
|         self.db_cursor.execute("SELECT name FROM tag_category WHERE LOWER(name) LIKE LOWER('%{0}%')".format(search)) | ||||
| 
 | ||||
|         rows = [] | ||||
|         for row in self.db_cursor.fetchall(): | ||||
|             rows.append(row[0]) | ||||
| 
 | ||||
|         return rows | ||||
| 
 | ||||
|     def search_fuzzy_tag(self, name: str, all_if_empty: bool = False) -> list: | ||||
|         """ | ||||
|         Search a list of tags fitting name fuzzy. | ||||
|         :param name: | ||||
|         :param all_if_empty: | ||||
|         :return: | ||||
|         """ | ||||
|         if all_if_empty and len(name) == 0: | ||||
|             self.db_cursor.execute("SELECT name, description, category FROM tag") | ||||
|         else: | ||||
|             self.db_cursor.execute("SELECT name, description, category FROM tag WHERE LOWER(name) LIKE LOWER('%{0}%')".format(name)) | ||||
| 
 | ||||
|         rows = self.db_cursor.fetchall() | ||||
|         new_rows = [] | ||||
|         for i in range(len(rows)): | ||||
|             new_rows.append([]) | ||||
|             for j in range(len(rows[i])): | ||||
|                 if rows[i][j] is None: | ||||
|                     new_rows[i].append("") | ||||
|                 else: | ||||
|                     new_rows[i].append(rows[i][j].strip()) | ||||
| 
 | ||||
|         return new_rows | ||||
| 
 | ||||
|     def search_fuzzy_artists(self, ID: int, description: str, all_if_empty: bool = False): | ||||
|         """ | ||||
|         Search a list of fitting artists fuzzy. | ||||
| 
 | ||||
|         If ID is None it will search using the description | ||||
|         :param ID: | ||||
|         :param description: | ||||
|         :param all_if_empty: | ||||
|         :return: | ||||
|         """ | ||||
|         if ID is not None: | ||||
|             self.db_cursor.execute("SELECT id, description FROM artist WHERE id = %(id)s", {"id": ID}) | ||||
|         elif all_if_empty and ID is None and len(description) == 0: | ||||
|             self.db_cursor.execute("SELECT id, description FROM artist") | ||||
|         else: | ||||
|             self.db_cursor.execute("SELECT id, description FROM artist WHERE LOWER(description) LIKE LOWER('%{0}%')" | ||||
|                                    .format(description)) | ||||
| 
 | ||||
|         return self.db_cursor.fetchall() | ||||
| 
 | ||||
|     def create_tag(self, name: str, description: str, aliases: list, implications: list, category: str = None): | ||||
|         name = name.strip() | ||||
|         if aliases is None: | ||||
|             aliases = [] | ||||
|         if implications is None: | ||||
|             implications = [] | ||||
|         d = {"name": name, "description": description, "category": category} | ||||
|         self.db_cursor.execute("INSERT INTO tag (name, description, category) " + | ||||
|                                "VALUES (LOWER(%(name)s), %(description)s, %(category)s)", d) | ||||
| 
 | ||||
|         for alias in aliases: | ||||
|             self.add_alias(name, alias) | ||||
|         for implicant in implications: | ||||
|             self.add_implication(name, implicant) | ||||
| 
 | ||||
|         self.db.commit() | ||||
| 
 | ||||
|     def edit_tag(self, name: str, description: str, aliases: list=None, implications: list=None, category: str = None, | ||||
|                  old_tag: str = None): | ||||
|         """ | ||||
|         Edit a tag with the new given data | ||||
|         :param name: unique tag name to apply edits to (distinct from old_tag) | ||||
|         :param description: new description to apply, set to None to omit | ||||
|         :param aliases: list of tag names to be the alias of this tag, set to None to omit | ||||
|         :param implications: list of tag names to be implied by this tag, set to None to omit | ||||
|         :param category: | ||||
|         :param old_tag: old tag name from which to transfer all data | ||||
|         :return: | ||||
|         """ | ||||
|         name = name.strip().lower() | ||||
|         d = { | ||||
|             "name": name, | ||||
|             "description": description, | ||||
|             "alias": aliases, | ||||
|             "implications": implications, | ||||
|             "category": category, | ||||
|         } | ||||
| 
 | ||||
|         if old_tag is not None and len(old_tag) > 0: | ||||
|             # tag name changed, need to transfer all data and remove old tag | ||||
|             old_tag = self.get_tag_by_name(old_tag) | ||||
|             d["ID"] = old_tag[0][-1] | ||||
|             self.db_cursor.execute("UPDATE tag SET name = %(name)s WHERE tag_ID = %(ID)s", d) | ||||
| 
 | ||||
|         if description is not None: | ||||
|             self.db_cursor.execute("UPDATE tag SET description = %(description)s, category = %(category)s " + | ||||
|                                    "WHERE name = %(name)s", d) | ||||
| 
 | ||||
|         if aliases is not None: | ||||
|             old_aliases = self.get_tag_aliases(name) | ||||
|             for alias in aliases: | ||||
|                 if alias in old_aliases:  # is this already set? | ||||
|                     continue | ||||
|                 self.add_alias(name, alias) | ||||
| 
 | ||||
|             for old_alias in old_aliases: | ||||
|                 if old_alias not in aliases:  # got to delete an alias? | ||||
|                     self.remove_alias(name, old_alias) | ||||
| 
 | ||||
|         if implications is not None: | ||||
|             old_implicants = self.get_tag_implications(name) | ||||
|             for implicant in implications: | ||||
|                 if implicant in old_implicants:  # is this already set? | ||||
|                     continue | ||||
|                 self.add_implication(name, implicant) | ||||
| 
 | ||||
|             for old_implicant in old_implicants: | ||||
|                 if old_implicant not in implications:  # got to delete an implicant? | ||||
|                     self.remove_implication(name, old_implicant) | ||||
| 
 | ||||
|     def add_alias(self, name: str, alias: str): | ||||
|         """ | ||||
|         Add the alias pair to the database | ||||
|         :param name:  | ||||
|         :param alias:  | ||||
|         :return:  | ||||
|         """ | ||||
|         d = { | ||||
|             "name": self.get_tag_ID(name), | ||||
|             "alias": self.get_tag_ID(alias) | ||||
|         } | ||||
|         self.db_cursor.execute("INSERT INTO tag_alias (tag1, tag2) VALUES (%(name)s, %(alias)s)", d) | ||||
| 
 | ||||
|     def remove_alias(self, name: str, alias: str): | ||||
|         """ | ||||
|         Remove alias pair from the database | ||||
|         :param name: | ||||
|         :param alias: | ||||
|         :return: | ||||
|         """ | ||||
|         d = { | ||||
|             "ID": self.get_tag_ID(name), | ||||
|             "alias": self.get_tag_ID(alias) | ||||
|         } | ||||
|         self.db_cursor.execute("DELETE FROM tag_alias WHERE (tag1 = %(ID)s and tag2 = %(alias)s) " + | ||||
|                                "or (tag1 = %(alias)s and tag2 = %(ID)s)", d) | ||||
| 
 | ||||
|     def add_implication(self, name: str, implicant: str): | ||||
|         """ | ||||
|         Add the implication to the database | ||||
|         :param name: | ||||
|         :param implicant: | ||||
|         :return: | ||||
|         """ | ||||
|         d = { | ||||
|             "name": self.get_tag_ID(name), | ||||
|             "implicant": self.get_tag_ID(implicant) | ||||
|         } | ||||
|         self.db_cursor.execute("INSERT INTO tag_implication (root_tag, implicate) VALUES (%(name)s, %(implicant)s)", | ||||
|                                d) | ||||
|         self.db.commit() | ||||
| 
 | ||||
|     def remove_implication(self, name: str, implicant: str): | ||||
|         """ | ||||
|         Remove the implication pair from the database | ||||
|         :param name: | ||||
|         :param implicant: | ||||
|         :return: | ||||
|         """ | ||||
|         d = {"name": self.get_tag_ID(name), | ||||
|              "implicant": self.get_tag_ID(implicant)} | ||||
|         self.db_cursor.execute("DELETE FROM tag_implication WHERE root_tag = %(name)s " + | ||||
|                                "and implicate = %(implicant)s", d) | ||||
|         self.db.commit() | ||||
| 
 | ||||
|     def remove_tag(self, name: str): | ||||
|         """ | ||||
|         Remove the given tag from the database | ||||
|         :param str: | ||||
|         :return: | ||||
|         """ | ||||
|         d = {"name": name} | ||||
|         self.db_cursor.execute("DELETE FROM tag WHERE name = %(name)s", d) | ||||
| 
 | ||||
|         self.db.commit() | ||||
| 
 | ||||
|     def get_tag_ID(self, name: str) -> int: | ||||
|         """ | ||||
|         Get the tag ID by querying it by its unique name | ||||
|         :param name: | ||||
|         :return: | ||||
|         """ | ||||
|         d = {"name": name} | ||||
|         self.db_cursor.execute("SELECT tag_ID, name FROM tag where LOWER(name) = LOWER(%(name)s)", d) | ||||
| 
 | ||||
|         rows = [] | ||||
|         for row in self.db_cursor.fetchall(): | ||||
|             rows.append(row[0]) | ||||
| 
 | ||||
|         if len(rows) > 1: | ||||
|             exit(1)  # something went horribly horribly wrong! | ||||
|         elif len(rows) == 0: | ||||
|             return None | ||||
| 
 | ||||
|         return rows[0] | ||||
| 
 | ||||
|     def get_tag_by_name(self, name: str) -> list: | ||||
|         """ | ||||
|         Search the tag in the DB via its name. | ||||
| 
 | ||||
|         Note: name is constrained to be unique by the DB | ||||
|         :param name: | ||||
|         :return: Returns the tag's name, description, category, tag ID | ||||
|         """ | ||||
|         d = {"name": name} | ||||
|         self.db_cursor.execute("SELECT name, description, category, tag_ID FROM tag where LOWER(name) = LOWER(%(name)s)", d) | ||||
| 
 | ||||
|         rows = [] | ||||
|         for row in self.db_cursor.fetchall(): | ||||
|             new_row = [] | ||||
|             for value in row: | ||||
|                 if value is None: | ||||
|                     new_row.append("") | ||||
|                 elif type(value) == str: | ||||
|                     new_row.append(value.strip()) | ||||
|                 else: | ||||
|                     new_row.append(value) | ||||
|             rows.append(new_row) | ||||
|         return rows | ||||
| 
 | ||||
|     def get_tag_by_ID(self, ID: int) -> list: | ||||
|         """ | ||||
|         Search the tag in the DB via its ID. | ||||
| 
 | ||||
|         Note: name is constrained to be unique by the DB | ||||
|         :param ID: | ||||
|         :return: Returns the tag's ID, name, description, category | ||||
|         """ | ||||
|         d = {"ID": ID} | ||||
|         self.db_cursor.execute("SELECT tag_ID, name, description, category FROM tag where tag_ID = %(ID)s", d) | ||||
| 
 | ||||
|         return self.db_cursor.fetchall() | ||||
| 
 | ||||
|     def get_tag_aliases(self, name: str) -> list: | ||||
|         """ | ||||
|         Search for the tag's aliases and the tag's aliases | ||||
|         :param name: | ||||
|         :return: List of tag Names that are aliases of this one | ||||
|         """ | ||||
|         aliases = self.get_tag_aliases_by_ID(self.get_tag_ID(name)) | ||||
|         result = [] | ||||
|         for alias in aliases: | ||||
|             tag_data = self.get_tag_by_ID(alias) | ||||
|             if len(tag_data) > 0: | ||||
|                 result.append(tag_data[0][1].strip()) | ||||
|         return result | ||||
| 
 | ||||
|     def get_tag_aliases_by_ID(self, tag_ID: int) -> list: | ||||
|         """ | ||||
|         Search for the tag's aliases and the tag's aliases | ||||
|         :param name: | ||||
|         :return: List of tag IDs that are aliases of this one | ||||
|         """ | ||||
|         marked = [] | ||||
|         to_search = [tag_ID] | ||||
|         while len(to_search) != 0: | ||||
|             curr_alias = to_search.pop() | ||||
|             found_aliases = self.__get_tag_aliases_no_recurse(curr_alias) | ||||
|             marked.append(curr_alias) | ||||
| 
 | ||||
|             for found_alias in found_aliases: | ||||
|                 if found_alias not in marked: | ||||
|                     to_search.append(found_alias) | ||||
| 
 | ||||
|         marked.remove(tag_ID) | ||||
|         return marked | ||||
| 
 | ||||
|     def get_all_tag_implications(self, name: str) -> list: | ||||
|         """ | ||||
|         Search for the tag's implications and those that are implied by them. | ||||
|         :param name: | ||||
|         :return: | ||||
|         """ | ||||
|         root_ID = self.get_tag_ID(name) | ||||
|         collected_tags = self.get_tag_implications_by_ID(root_ID) | ||||
|         collected_tags.append(root_ID) | ||||
|         to_search = self.get_tag_implications_by_ID(root_ID) | ||||
| 
 | ||||
|         while len(to_search) != 0: | ||||
|             curr_tag = to_search.pop() | ||||
|             found_implications = self.get_tag_implications_by_ID(curr_tag) | ||||
|             for found in found_implications: | ||||
|                 if found in collected_tags: | ||||
|                     continue | ||||
|                 else: | ||||
|                     collected_tags.append(found) | ||||
|                     to_search.append(found) | ||||
| 
 | ||||
|         collected_tags.remove(root_ID) | ||||
| 
 | ||||
|         result = [] | ||||
|         for tag_ID in collected_tags: | ||||
|             tag_data = self.get_tag_by_ID(tag_ID) | ||||
|             if len(tag_data) != 0: | ||||
|                 result.append(tag_data[0][1].strip()) | ||||
|         return result | ||||
| 
 | ||||
|     def get_tag_implications_by_ID(self, ID: int) -> list: | ||||
|         """ | ||||
|         Search for the tag's implications | ||||
|         :param ID: ID of the tag | ||||
|         :return: returns a list of IDs that are implied | ||||
|         """ | ||||
|         d = {"ID": ID} | ||||
|         if d["ID"] is None: | ||||
|             return [] | ||||
| 
 | ||||
|         self.db_cursor.execute("SELECT root_tag, implicate FROM Tag_Implication WHERE root_tag = %(ID)s", d) | ||||
| 
 | ||||
|         rows = self.db_cursor.fetchall() | ||||
|         if len(rows) == 0: | ||||
|             return [] | ||||
| 
 | ||||
|         r = [] | ||||
|         for row in rows: | ||||
|             r.append(row[1]) | ||||
| 
 | ||||
|         return r | ||||
| 
 | ||||
|     def get_tag_implications(self, name: str) -> list: | ||||
|         """ | ||||
|         Search for the tag's implications | ||||
|         :param name: | ||||
|         :param output_ID: Don't resolve the tag ids into names in the resulting list | ||||
|         :return: List of tag names | ||||
|         """ | ||||
|         d = {"ID": self.get_tag_ID(name)} | ||||
|         if d["ID"] is None: | ||||
|             return [] | ||||
| 
 | ||||
|         self.db_cursor.execute("SELECT root_tag, implicate FROM Tag_Implication WHERE root_tag = %(ID)s", d) | ||||
| 
 | ||||
|         rows = self.db_cursor.fetchall() | ||||
|         if len(rows) == 0: | ||||
|             return [] | ||||
| 
 | ||||
|         r = [] | ||||
|         for row in rows: | ||||
|             r.append(row[1]) | ||||
| 
 | ||||
|         result = [self.get_tag_by_ID(v) for v in r] | ||||
| 
 | ||||
|         r = [] | ||||
|         for value in result: | ||||
|             r.append(value[0][1].strip()) | ||||
| 
 | ||||
|         return r | ||||
| 
 | ||||
|     def get_tag_impliers(self, name: str) -> list: | ||||
|         """ | ||||
|         Search for tags that imply this one. | ||||
|         :param name: | ||||
|         :return: | ||||
|         """ | ||||
|         d = {"ID": self.get_tag_ID(name)} | ||||
|         self.db_cursor.execute("SELECT root_tag, implicate FROM Tag_Implication WHERE implicate = %(ID)s", d) | ||||
| 
 | ||||
|         rows = self.db_cursor.fetchall() | ||||
|         r = [] | ||||
|         for row in rows: | ||||
|             r.append(row[0]) | ||||
|         return r | ||||
| 
 | ||||
|     def __get_tag_aliases_no_recurse(self, tag_id: int) -> list: | ||||
|         """ | ||||
|         Search for the tag's aliases | ||||
|         :param tag_id: | ||||
|         :return: | ||||
|         """ | ||||
|         d = {"ID": tag_id} | ||||
|         if d["ID"] is None: | ||||
|             return [] | ||||
| 
 | ||||
|         self.db_cursor.execute("SELECT tag1, tag2 FROM Tag_Alias " | ||||
|                                "WHERE tag1 = %(ID)s or tag2 = %(ID)s", d) | ||||
|         rows = self.db_cursor.fetchall() | ||||
|         r = [] | ||||
|         for row in rows: | ||||
|             if row[0] == tag_id: | ||||
|                 r.append(row[1]) | ||||
|             elif row[1] == tag_id: | ||||
|                 r.append(row[0]) | ||||
|             else: | ||||
|                 raise Exception("Something went terribly wrong!") | ||||
| 
 | ||||
|         return r | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     db = DBAdapter(user="artnet_editor", password="G606Rm9sFEXe6wfTLxVu", | ||||
|                    database="artnet", host="localhost", port=5432) | ||||
| 
 | ||||
|     search = 'Ajin/66699b4e41f533982db1766e8f72494a.gif2222' | ||||
|     print("Art search result:", search, db.get_art_by_path('Ajin/66699b4e41f533982db1766e8f72494a.gif')) | ||||
| 
 | ||||
|     search = "tag" | ||||
|     print("Fuzzy Search Result:", search, db.search_fuzzy_tag(search)) | ||||
| 
 | ||||
|     search = "tag1" | ||||
|     print("Search Result:", search, db.get_tag_by_name(search)) | ||||
| 
 | ||||
|     search = "tag1" | ||||
|     print("Alias Search Result:", search, db.get_tag_aliases(search)) | ||||
| 
 | ||||
|     search = "tag1" | ||||
|     print("Implication Search Result:", search, db.get_tag_implications(search)) | ||||
| 
 | ||||
|     target = "tag1" | ||||
|     db.edit_tag(target, "new description! ;D", aliases=["tag1_alias"], implications=["tag1_implicated"]) | ||||
|     print("Editing:", target, db.get_tag_by_name(target), db.get_tag_aliases(target), db.get_tag_implications(target)) | ||||
| 
 | ||||
| @ -0,0 +1,135 @@ | ||||
| import os | ||||
| import base64 | ||||
| import copy | ||||
| 
 | ||||
| import yaml | ||||
| from cryptography.exceptions import AlreadyFinalized | ||||
| from cryptography.exceptions import InvalidTag | ||||
| from cryptography.exceptions import UnsupportedAlgorithm | ||||
| from cryptography.hazmat.backends import default_backend | ||||
| from cryptography.hazmat.primitives import hashes | ||||
| from cryptography.hazmat.primitives.ciphers.aead import AESGCM | ||||
| from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC | ||||
| 
 | ||||
| 
 | ||||
| class ConfigReader: | ||||
| 
 | ||||
|     kdf = None | ||||
|     nonce = None | ||||
| 
 | ||||
|     def __init__(self, root_folder: str, password: str): | ||||
|         if root_folder is None: | ||||
|             raise Exception("No root folder was defined!") | ||||
|         self.__key = None | ||||
|         self.__aesgcm = None | ||||
|         self.data = {} | ||||
| 
 | ||||
|         self.__password = password | ||||
|         self.__config_location = os.path.join(root_folder, ".artnet", "artnet.config") | ||||
| 
 | ||||
|         ConfigReader.__create_kdf() | ||||
|         self.__key = ConfigReader.kdf.derive(bytes(password.encode('utf-8'))) | ||||
|         self.__aesgcm = AESGCM(self.__key) | ||||
| 
 | ||||
|         if os.path.exists(self.__config_location) and os.path.isfile(self.__config_location): | ||||
|             self.read_config() | ||||
|         else: | ||||
|             if not os.path.exists(os.path.join(root_folder, ".artnet")): | ||||
|                 os.mkdir(os.path.join(root_folder, ".artnet")) | ||||
|             self.create_default_config() | ||||
|             self.read_config() | ||||
| 
 | ||||
|     def update_config(self): | ||||
|         """ | ||||
|         Update the written config with the local settings | ||||
|         :return: | ||||
|         """ | ||||
|         file = open(self.__config_location, "w") | ||||
|         yaml.dump(stream=file, data=self.encrypt_sensitive_data(copy.deepcopy(self.data))) | ||||
| 
 | ||||
|     def read_config(self): | ||||
|         """ | ||||
|         Read the config from file and overwrite local settings | ||||
|         :return: | ||||
|         """ | ||||
|         print(os.path.isfile(self.__config_location)) | ||||
|         print(os.path.join(os.getcwd(), self.__config_location)) | ||||
|         file = open(self.__config_location, "r") | ||||
|         data = yaml.safe_load(stream=file) | ||||
|         self.data = self.decrypt_sensitive_data(data) | ||||
| 
 | ||||
|     def encrypt_sensitive_data(self, data: dict) -> dict: | ||||
|         """ | ||||
|         Encrypts the sensitive portions of the data | ||||
|         :return: | ||||
|         """ | ||||
|         new_data = data | ||||
|         new_data["db"]["password"] = self.encrypt_text(data["db"]["password"]) | ||||
|         new_data["db"]["user"] = self.encrypt_text(data["db"]["user"]) | ||||
| 
 | ||||
|         return new_data | ||||
| 
 | ||||
|     def decrypt_sensitive_data(self, data: dict) -> dict: | ||||
|         """ | ||||
|         Decrypts the sensitive portions of the data | ||||
|         :return: | ||||
|         """ | ||||
|         new_data = data | ||||
|         new_data["db"]["password"] = self.decrypt_text(data["db"]["password"]) | ||||
|         new_data["db"]["user"] = self.decrypt_text(data["db"]["user"]) | ||||
| 
 | ||||
|         return new_data | ||||
| 
 | ||||
|     def create_default_config(self): | ||||
|         """ | ||||
|         Create a default config, overwrites all settings and generates a new key | ||||
|         :return: | ||||
|         """ | ||||
|         self.data = { | ||||
|             "db": { | ||||
|                 "host": "localhost", | ||||
|                 "port": 5432, | ||||
|                 "database": "artnet", | ||||
|                 "user": "your_user", | ||||
|                 "password": "enter_password_via_gui" | ||||
|             }, | ||||
| 
 | ||||
|         } | ||||
|         self.update_config() | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def __create_kdf(): | ||||
|         ConfigReader.kdf = PBKDF2HMAC(algorithm=hashes.SHA512(), | ||||
|                                       length=32, | ||||
|                                       salt=bytes("ArtN3t.WhatElse?".encode('utf-8')), | ||||
|                                       iterations=10000, | ||||
|                                       backend=default_backend() | ||||
|                                       ) | ||||
|         ConfigReader.nonce = bytes("qt34nvßn".encode('utf-8')) | ||||
| 
 | ||||
|     def encrypt_text(self, text: str) -> str: | ||||
|         cipher_text_bytes = self.__aesgcm.encrypt(data=text.encode('utf-8'), | ||||
|                                            associated_data=None, | ||||
|                                            nonce=ConfigReader.nonce | ||||
|                                            ) | ||||
|         return base64.urlsafe_b64encode(cipher_text_bytes) | ||||
| 
 | ||||
|     def decrypt_text(self, cipher: str) -> str: | ||||
|         if ConfigReader.kdf is None: | ||||
|             ConfigReader.__create_kdf() | ||||
|         try: | ||||
|             decrypted_cipher_text_bytes = self.__aesgcm.decrypt( | ||||
|                 nonce=ConfigReader.nonce, | ||||
|                 data=base64.urlsafe_b64decode(cipher), | ||||
|                 associated_data=None | ||||
|             ) | ||||
|         except InvalidTag: | ||||
|             raise Exception("Could not decrypt Text! Wrong Password?") | ||||
| 
 | ||||
|         return decrypted_cipher_text_bytes.decode('utf-8') | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     cr = ConfigReader(password="MySuperDuperKey", root_folder=".") | ||||
| 
 | ||||
|     print(cr.data) | ||||
| @ -0,0 +1,45 @@ | ||||
| import os | ||||
| 
 | ||||
| 
 | ||||
| class FileReader: | ||||
| 
 | ||||
|     def __init__(self, root: str): | ||||
|         if not os.path.isdir(root): | ||||
|             raise Exception("Root was not a valid directory! {0}".format(root)) | ||||
|         self.__root = root | ||||
| 
 | ||||
|     def list_artists(self) -> list: | ||||
|         """ | ||||
|         List all Artists within the root folder | ||||
|         :return: | ||||
|         """ | ||||
|         l = [os.path.basename(x) for x in os.listdir(self.__root)] | ||||
|         if ".artnet" in l: | ||||
|             l.remove(".artnet") | ||||
|         return l | ||||
| 
 | ||||
|     def get_files(self, artist: str) -> list: | ||||
|         """ | ||||
|         List all files within the given artists directory with their relative path (to the root). | ||||
|         :param artist: | ||||
|         :return: | ||||
|         """ | ||||
|         l = [] | ||||
| 
 | ||||
|         root = os.path.join(self.__root) | ||||
|         dirs = [artist] | ||||
| 
 | ||||
|         while len(dirs) != 0: | ||||
|             curr_dir = dirs.pop() | ||||
|             curr_full_dir = os.path.join(root, curr_dir) | ||||
|             l += [os.path.join(curr_dir, f) for f in os.listdir(curr_full_dir) if os.path.isfile(os.path.join(curr_full_dir, f))] | ||||
|             for d in [x for x in os.listdir(curr_full_dir) if os.path.isdir(os.path.join(curr_full_dir, x))]: | ||||
|                 dirs.append(os.path.join(curr_dir, d)) | ||||
| 
 | ||||
|         return l | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     fr = FileReader("/home/peery/Software_Projects/ArtNet/App/Fake_Other_Artists") | ||||
|     print(fr.list_artists()) | ||||
|     print(fr.get_files("AkuDrache")) | ||||
| @ -0,0 +1,57 @@ | ||||
| from PyQt5 import QtWidgets | ||||
| from PyQt5.QtCore import Qt | ||||
| 
 | ||||
| from ArtNet.gui.dialogs.artist_modify_dialog.artist_modify_dialog import Ui_Artist_Mod_Dialog | ||||
| 
 | ||||
| 
 | ||||
| class ArtistModDialog(QtWidgets.QDialog): | ||||
| 
 | ||||
|     def __init__(self, parent=None, create_artist: bool = False): | ||||
|         super().__init__(parent) | ||||
|         self.parent = parent | ||||
|         self.ui = Ui_Artist_Mod_Dialog() | ||||
|         self.ui.setupUi(self) | ||||
| 
 | ||||
|         self.data = {"id": None, | ||||
|                      "description": None} | ||||
| 
 | ||||
|         if not create_artist: | ||||
|             self.set_available_artists(self.parent.parent.parent.get_all_artists()) | ||||
|         else: | ||||
|             self.ui.id_list = QtWidgets.QLineEdit(self) | ||||
|             self.ui.id_spinbox.setParent(None) | ||||
|             self.ui.id_spinbox.destroy() | ||||
|             self.ui.id_layout.addWidget(self.ui.id_list) | ||||
| 
 | ||||
|         self.ui.description_line.textChanged.connect(self.on_description_changed) | ||||
| 
 | ||||
|     def set_available_artists(self, artists: list): | ||||
|         if artists is None: | ||||
|             self.ui.id_spinbox.setParent(None) | ||||
|             self.ui.id_spinbox.destroy() | ||||
|             self.ui.id_label.setText("Artist ID (auto-generated)") | ||||
|             return | ||||
| 
 | ||||
|         self.ui.id_spinbox.setMinimum(0) | ||||
|         self.ui.id_spinbox.setMaximum(len(artists)) | ||||
|         self.ui.id_spinbox.valueChanged.connect(self.on_id_spinbox_changed) | ||||
| 
 | ||||
|     def on_id_spinbox_changed(self): | ||||
|         id = self.ui.id_spinbox.value() | ||||
|         artist = self.parent.parent.parent.get_artist(id) | ||||
|         if len(artist) != 0: | ||||
|             self.ui.description_line.setText(artist[0][1]) | ||||
|         else: | ||||
|             self.ui.description_line.setText("Warning! unknown Artist ID!") | ||||
| 
 | ||||
|         self.data["id"] = id | ||||
| 
 | ||||
|     def on_description_changed(self): | ||||
|         self.data["description"] = self.ui.description_line.text() | ||||
| 
 | ||||
|     def exec_(self): | ||||
|         result = super(ArtistModDialog, self).exec_() | ||||
|         if result == QtWidgets.QDialog.Rejected: | ||||
|             return None | ||||
| 
 | ||||
|         return self.data | ||||
| @ -0,0 +1,101 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| 
 | ||||
| # Form implementation generated from reading ui file 'dialogs/artist_modify_dialog/artist_modify_dialog.ui' | ||||
| # | ||||
| # Created by: PyQt5 UI code generator 5.15.0 | ||||
| # | ||||
| # WARNING: Any manual changes made to this file will be lost when pyuic5 is | ||||
| # run again.  Do not edit this file unless you know what you are doing. | ||||
| 
 | ||||
| 
 | ||||
| from PyQt5 import QtCore, QtGui, QtWidgets | ||||
| 
 | ||||
| 
 | ||||
| class Ui_Artist_Mod_Dialog(object): | ||||
|     def setupUi(self, Artist_Mod_Dialog): | ||||
|         Artist_Mod_Dialog.setObjectName("Artist_Mod_Dialog") | ||||
|         Artist_Mod_Dialog.resize(311, 205) | ||||
|         self.verticalLayout = QtWidgets.QVBoxLayout(Artist_Mod_Dialog) | ||||
|         self.verticalLayout.setObjectName("verticalLayout") | ||||
|         self.dialog_frame = QtWidgets.QFrame(Artist_Mod_Dialog) | ||||
|         self.dialog_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.dialog_frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.dialog_frame.setObjectName("dialog_frame") | ||||
|         self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.dialog_frame) | ||||
|         self.verticalLayout_2.setObjectName("verticalLayout_2") | ||||
|         self.dialog_topic = QtWidgets.QLabel(self.dialog_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.dialog_topic.sizePolicy().hasHeightForWidth()) | ||||
|         self.dialog_topic.setSizePolicy(sizePolicy) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(11) | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.dialog_topic.setFont(font) | ||||
|         self.dialog_topic.setAlignment(QtCore.Qt.AlignCenter) | ||||
|         self.dialog_topic.setObjectName("dialog_topic") | ||||
|         self.verticalLayout_2.addWidget(self.dialog_topic) | ||||
|         self.frame = QtWidgets.QFrame(self.dialog_frame) | ||||
|         self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.frame.setObjectName("frame") | ||||
|         self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.frame) | ||||
|         self.verticalLayout_5.setObjectName("verticalLayout_5") | ||||
|         self.id_label = QtWidgets.QLabel(self.frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.id_label.sizePolicy().hasHeightForWidth()) | ||||
|         self.id_label.setSizePolicy(sizePolicy) | ||||
|         self.id_label.setObjectName("id_label") | ||||
|         self.verticalLayout_5.addWidget(self.id_label) | ||||
|         self.id_layout = QtWidgets.QVBoxLayout() | ||||
|         self.id_layout.setObjectName("id_layout") | ||||
|         self.id_spinbox = QtWidgets.QDoubleSpinBox(self.frame) | ||||
|         self.id_spinbox.setDecimals(0) | ||||
|         self.id_spinbox.setMaximum(10000.0) | ||||
|         self.id_spinbox.setObjectName("id_spinbox") | ||||
|         self.id_layout.addWidget(self.id_spinbox) | ||||
|         self.verticalLayout_5.addLayout(self.id_layout) | ||||
|         self.description_label = QtWidgets.QLabel(self.frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.description_label.sizePolicy().hasHeightForWidth()) | ||||
|         self.description_label.setSizePolicy(sizePolicy) | ||||
|         self.description_label.setObjectName("description_label") | ||||
|         self.verticalLayout_5.addWidget(self.description_label) | ||||
|         self.description_line = QtWidgets.QLineEdit(self.frame) | ||||
|         self.description_line.setObjectName("description_line") | ||||
|         self.verticalLayout_5.addWidget(self.description_line) | ||||
|         self.verticalLayout_2.addWidget(self.frame) | ||||
|         self.verticalLayout.addWidget(self.dialog_frame) | ||||
|         self.buttonBox = QtWidgets.QDialogButtonBox(Artist_Mod_Dialog) | ||||
|         self.buttonBox.setOrientation(QtCore.Qt.Horizontal) | ||||
|         self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) | ||||
|         self.buttonBox.setObjectName("buttonBox") | ||||
|         self.verticalLayout.addWidget(self.buttonBox) | ||||
| 
 | ||||
|         self.retranslateUi(Artist_Mod_Dialog) | ||||
|         self.buttonBox.accepted.connect(Artist_Mod_Dialog.accept) | ||||
|         self.buttonBox.rejected.connect(Artist_Mod_Dialog.reject) | ||||
|         QtCore.QMetaObject.connectSlotsByName(Artist_Mod_Dialog) | ||||
| 
 | ||||
|     def retranslateUi(self, Artist_Mod_Dialog): | ||||
|         _translate = QtCore.QCoreApplication.translate | ||||
|         Artist_Mod_Dialog.setWindowTitle(_translate("Artist_Mod_Dialog", "Dialog")) | ||||
|         self.dialog_topic.setText(_translate("Artist_Mod_Dialog", "Artist Topic")) | ||||
|         self.id_label.setText(_translate("Artist_Mod_Dialog", "Artist ID:")) | ||||
|         self.description_label.setText(_translate("Artist_Mod_Dialog", "Artist Description:")) | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     import sys | ||||
|     app = QtWidgets.QApplication(sys.argv) | ||||
|     Artist_Mod_Dialog = QtWidgets.QDialog() | ||||
|     ui = Ui_Artist_Mod_Dialog() | ||||
|     ui.setupUi(Artist_Mod_Dialog) | ||||
|     Artist_Mod_Dialog.show() | ||||
|     sys.exit(app.exec_()) | ||||
| @ -0,0 +1,154 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>Artist_Mod_Dialog</class> | ||||
|  <widget class="QDialog" name="Artist_Mod_Dialog"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>311</width> | ||||
|     <height>205</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Dialog</string> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|    <item> | ||||
|     <widget class="QFrame" name="dialog_frame"> | ||||
|      <property name="frameShape"> | ||||
|       <enum>QFrame::StyledPanel</enum> | ||||
|      </property> | ||||
|      <property name="frameShadow"> | ||||
|       <enum>QFrame::Raised</enum> | ||||
|      </property> | ||||
|      <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="dialog_topic"> | ||||
|         <property name="sizePolicy"> | ||||
|          <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|           <horstretch>0</horstretch> | ||||
|           <verstretch>0</verstretch> | ||||
|          </sizepolicy> | ||||
|         </property> | ||||
|         <property name="font"> | ||||
|          <font> | ||||
|           <pointsize>11</pointsize> | ||||
|           <weight>75</weight> | ||||
|           <bold>true</bold> | ||||
|          </font> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Artist Topic</string> | ||||
|         </property> | ||||
|         <property name="alignment"> | ||||
|          <set>Qt::AlignCenter</set> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QFrame" name="frame"> | ||||
|         <property name="frameShape"> | ||||
|          <enum>QFrame::StyledPanel</enum> | ||||
|         </property> | ||||
|         <property name="frameShadow"> | ||||
|          <enum>QFrame::Raised</enum> | ||||
|         </property> | ||||
|         <layout class="QVBoxLayout" name="verticalLayout_5"> | ||||
|          <item> | ||||
|           <widget class="QLabel" name="id_label"> | ||||
|            <property name="sizePolicy"> | ||||
|             <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|              <horstretch>0</horstretch> | ||||
|              <verstretch>0</verstretch> | ||||
|             </sizepolicy> | ||||
|            </property> | ||||
|            <property name="text"> | ||||
|             <string>Artist ID:</string> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <layout class="QVBoxLayout" name="id_layout"> | ||||
|            <item> | ||||
|             <widget class="QDoubleSpinBox" name="id_spinbox"> | ||||
|              <property name="decimals"> | ||||
|               <number>0</number> | ||||
|              </property> | ||||
|              <property name="maximum"> | ||||
|               <double>10000.000000000000000</double> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|           </layout> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QLabel" name="description_label"> | ||||
|            <property name="sizePolicy"> | ||||
|             <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|              <horstretch>0</horstretch> | ||||
|              <verstretch>0</verstretch> | ||||
|             </sizepolicy> | ||||
|            </property> | ||||
|            <property name="text"> | ||||
|             <string>Artist Description:</string> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QLineEdit" name="description_line"/> | ||||
|          </item> | ||||
|         </layout> | ||||
|        </widget> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QDialogButtonBox" name="buttonBox"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Horizontal</enum> | ||||
|      </property> | ||||
|      <property name="standardButtons"> | ||||
|       <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>accepted()</signal> | ||||
|    <receiver>Artist_Mod_Dialog</receiver> | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>248</x> | ||||
|      <y>254</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>157</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>rejected()</signal> | ||||
|    <receiver>Artist_Mod_Dialog</receiver> | ||||
|    <slot>reject()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>316</x> | ||||
|      <y>260</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>286</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|  </connections> | ||||
| </ui> | ||||
| @ -0,0 +1,69 @@ | ||||
| from PyQt5 import QtWidgets | ||||
| from PyQt5.QtCore import Qt | ||||
| from PyQt5.QtGui import QStandardItem, QStandardItemModel | ||||
| 
 | ||||
| from ArtNet.gui.dialogs.category_modify_dialog.category_modify_dialog import Ui_category_modify_dialog | ||||
| 
 | ||||
| 
 | ||||
| class CategoryModDialog(QtWidgets.QDialog): | ||||
| 
 | ||||
|     def __init__(self, parent=None, delete_category: bool = False): | ||||
|         super().__init__(parent) | ||||
|         self.parent = parent | ||||
| 
 | ||||
|         self.ui = Ui_category_modify_dialog() | ||||
|         self.ui.setupUi(self) | ||||
|         self.name_valid: bool = False | ||||
|         self.all_categories: list = None | ||||
| 
 | ||||
|         self.default_style = self.ui.category_name_line.styleSheet() | ||||
| 
 | ||||
|         self.delete_mode: bool = delete_category | ||||
|         if delete_category: | ||||
|             self.ui.dialog_topic.setText("Delete Category") | ||||
|         else: | ||||
|             self.ui.dialog_topic.setText("Create Category") | ||||
| 
 | ||||
|         self.ui.category_name_line.textChanged.connect(self.on_category_name_change) | ||||
|         self.set_all_categories() | ||||
| 
 | ||||
|     def set_all_categories(self): | ||||
|         """ | ||||
|         Populate the category list with all categories if name line is empty. | ||||
|         Otherwise fuzzy search for fitting categories | ||||
|         :return: | ||||
|         """ | ||||
|         self.all_categories = self.parent.get_categories(self.ui.category_name_line.text(), all_if_empty=True) | ||||
|         categories = [] | ||||
|         for i in range(len(self.all_categories)): | ||||
|             categories.append(self.all_categories[i]) | ||||
|         self.all_categories = categories | ||||
| 
 | ||||
|         item_model = QStandardItemModel(self.ui.other_categories_list) | ||||
| 
 | ||||
|         for category in self.all_categories: | ||||
|             item = QStandardItem(category) | ||||
|             item_model.appendRow(item) | ||||
| 
 | ||||
|         self.ui.other_categories_list.setModel(item_model) | ||||
| 
 | ||||
|     def on_category_name_change(self): | ||||
|         # TODO mark input red if it is a taken name (or if it isn't in deletion mode) | ||||
|         self.set_all_categories() | ||||
|         self.ui.category_name_line.setStyleSheet(self.default_style) | ||||
|         self.name_valid = True | ||||
| 
 | ||||
|         if self.delete_mode: | ||||
|             if self.ui.category_name_line.text() not in self.all_categories: | ||||
|                 self.ui.category_name_line.setStyleSheet("color: red") | ||||
|                 self.name_valid = False | ||||
|         else: | ||||
|             if self.ui.category_name_line.text() in self.all_categories: | ||||
|                 self.ui.category_name_line.setStyleSheet("color: red") | ||||
|                 self.name_valid = False | ||||
| 
 | ||||
|     def exec_(self) -> dict: | ||||
|         if super(CategoryModDialog, self).exec_() == QtWidgets.QDialog.Rejected or not self.name_valid: | ||||
|             return None | ||||
| 
 | ||||
|         return {"name": self.ui.category_name_line.text()} | ||||
| @ -0,0 +1,92 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| 
 | ||||
| # Form implementation generated from reading ui file 'dialogs/category_modify_dialog/category_modify_dialog.ui' | ||||
| # | ||||
| # Created by: PyQt5 UI code generator 5.15.0 | ||||
| # | ||||
| # WARNING: Any manual changes made to this file will be lost when pyuic5 is | ||||
| # run again.  Do not edit this file unless you know what you are doing. | ||||
| 
 | ||||
| 
 | ||||
| from PyQt5 import QtCore, QtGui, QtWidgets | ||||
| 
 | ||||
| 
 | ||||
| class Ui_category_modify_dialog(object): | ||||
|     def setupUi(self, category_modify_dialog): | ||||
|         category_modify_dialog.setObjectName("category_modify_dialog") | ||||
|         category_modify_dialog.resize(398, 298) | ||||
|         self.verticalLayout = QtWidgets.QVBoxLayout(category_modify_dialog) | ||||
|         self.verticalLayout.setObjectName("verticalLayout") | ||||
|         self.frame = QtWidgets.QFrame(category_modify_dialog) | ||||
|         self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.frame.setObjectName("frame") | ||||
|         self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.frame) | ||||
|         self.verticalLayout_2.setObjectName("verticalLayout_2") | ||||
|         self.dialog_topic = QtWidgets.QLabel(self.frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.dialog_topic.sizePolicy().hasHeightForWidth()) | ||||
|         self.dialog_topic.setSizePolicy(sizePolicy) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(12) | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.dialog_topic.setFont(font) | ||||
|         self.dialog_topic.setAlignment(QtCore.Qt.AlignCenter) | ||||
|         self.dialog_topic.setObjectName("dialog_topic") | ||||
|         self.verticalLayout_2.addWidget(self.dialog_topic) | ||||
|         self.frame_2 = QtWidgets.QFrame(self.frame) | ||||
|         self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.frame_2.setObjectName("frame_2") | ||||
|         self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.frame_2) | ||||
|         self.verticalLayout_3.setObjectName("verticalLayout_3") | ||||
|         self.category_name_label = QtWidgets.QLabel(self.frame_2) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.category_name_label.sizePolicy().hasHeightForWidth()) | ||||
|         self.category_name_label.setSizePolicy(sizePolicy) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(10) | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.category_name_label.setFont(font) | ||||
|         self.category_name_label.setObjectName("category_name_label") | ||||
|         self.verticalLayout_3.addWidget(self.category_name_label) | ||||
|         self.category_name_line = QtWidgets.QLineEdit(self.frame_2) | ||||
|         self.category_name_line.setObjectName("category_name_line") | ||||
|         self.verticalLayout_3.addWidget(self.category_name_line) | ||||
|         self.other_categories_list = QtWidgets.QListView(self.frame_2) | ||||
|         self.other_categories_list.setObjectName("other_categories_list") | ||||
|         self.verticalLayout_3.addWidget(self.other_categories_list) | ||||
|         self.verticalLayout_2.addWidget(self.frame_2) | ||||
|         self.verticalLayout.addWidget(self.frame) | ||||
|         self.buttonBox = QtWidgets.QDialogButtonBox(category_modify_dialog) | ||||
|         self.buttonBox.setOrientation(QtCore.Qt.Horizontal) | ||||
|         self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) | ||||
|         self.buttonBox.setObjectName("buttonBox") | ||||
|         self.verticalLayout.addWidget(self.buttonBox) | ||||
| 
 | ||||
|         self.retranslateUi(category_modify_dialog) | ||||
|         self.buttonBox.accepted.connect(category_modify_dialog.accept) | ||||
|         self.buttonBox.rejected.connect(category_modify_dialog.reject) | ||||
|         QtCore.QMetaObject.connectSlotsByName(category_modify_dialog) | ||||
| 
 | ||||
|     def retranslateUi(self, category_modify_dialog): | ||||
|         _translate = QtCore.QCoreApplication.translate | ||||
|         category_modify_dialog.setWindowTitle(_translate("category_modify_dialog", "Dialog")) | ||||
|         self.dialog_topic.setText(_translate("category_modify_dialog", "Category Topic")) | ||||
|         self.category_name_label.setText(_translate("category_modify_dialog", "Category Name:")) | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     import sys | ||||
|     app = QtWidgets.QApplication(sys.argv) | ||||
|     category_modify_dialog = QtWidgets.QDialog() | ||||
|     ui = Ui_category_modify_dialog() | ||||
|     ui.setupUi(category_modify_dialog) | ||||
|     category_modify_dialog.show() | ||||
|     sys.exit(app.exec_()) | ||||
| @ -0,0 +1,137 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>category_modify_dialog</class> | ||||
|  <widget class="QDialog" name="category_modify_dialog"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>398</width> | ||||
|     <height>298</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Dialog</string> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|    <item> | ||||
|     <widget class="QFrame" name="frame"> | ||||
|      <property name="frameShape"> | ||||
|       <enum>QFrame::StyledPanel</enum> | ||||
|      </property> | ||||
|      <property name="frameShadow"> | ||||
|       <enum>QFrame::Raised</enum> | ||||
|      </property> | ||||
|      <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="dialog_topic"> | ||||
|         <property name="sizePolicy"> | ||||
|          <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|           <horstretch>0</horstretch> | ||||
|           <verstretch>0</verstretch> | ||||
|          </sizepolicy> | ||||
|         </property> | ||||
|         <property name="font"> | ||||
|          <font> | ||||
|           <pointsize>12</pointsize> | ||||
|           <weight>75</weight> | ||||
|           <bold>true</bold> | ||||
|          </font> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Category Topic</string> | ||||
|         </property> | ||||
|         <property name="alignment"> | ||||
|          <set>Qt::AlignCenter</set> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QFrame" name="frame_2"> | ||||
|         <property name="frameShape"> | ||||
|          <enum>QFrame::StyledPanel</enum> | ||||
|         </property> | ||||
|         <property name="frameShadow"> | ||||
|          <enum>QFrame::Raised</enum> | ||||
|         </property> | ||||
|         <layout class="QVBoxLayout" name="verticalLayout_3"> | ||||
|          <item> | ||||
|           <widget class="QLabel" name="category_name_label"> | ||||
|            <property name="sizePolicy"> | ||||
|             <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|              <horstretch>0</horstretch> | ||||
|              <verstretch>0</verstretch> | ||||
|             </sizepolicy> | ||||
|            </property> | ||||
|            <property name="font"> | ||||
|             <font> | ||||
|              <pointsize>10</pointsize> | ||||
|              <weight>75</weight> | ||||
|              <bold>true</bold> | ||||
|             </font> | ||||
|            </property> | ||||
|            <property name="text"> | ||||
|             <string>Category Name:</string> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QLineEdit" name="category_name_line"/> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QListView" name="other_categories_list"/> | ||||
|          </item> | ||||
|         </layout> | ||||
|        </widget> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QDialogButtonBox" name="buttonBox"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Horizontal</enum> | ||||
|      </property> | ||||
|      <property name="standardButtons"> | ||||
|       <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>accepted()</signal> | ||||
|    <receiver>category_modify_dialog</receiver> | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>248</x> | ||||
|      <y>254</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>157</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>rejected()</signal> | ||||
|    <receiver>category_modify_dialog</receiver> | ||||
|    <slot>reject()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>316</x> | ||||
|      <y>260</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>286</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|  </connections> | ||||
| </ui> | ||||
| @ -0,0 +1,147 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| 
 | ||||
| # Form implementation generated from reading ui file 'dialogs/db_connection_dialog/db_connection_dialog.ui' | ||||
| # | ||||
| # Created by: PyQt5 UI code generator 5.15.0 | ||||
| # | ||||
| # WARNING: Any manual changes made to this file will be lost when pyuic5 is | ||||
| # run again.  Do not edit this file unless you know what you are doing. | ||||
| 
 | ||||
| 
 | ||||
| from PyQt5 import QtCore, QtGui, QtWidgets | ||||
| 
 | ||||
| 
 | ||||
| class Ui_DBConnection(object): | ||||
|     def setupUi(self, DBConnection): | ||||
|         DBConnection.setObjectName("DBConnection") | ||||
|         DBConnection.resize(328, 262) | ||||
|         self.verticalLayout = QtWidgets.QVBoxLayout(DBConnection) | ||||
|         self.verticalLayout.setObjectName("verticalLayout") | ||||
|         self.dialog_frame = QtWidgets.QFrame(DBConnection) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.dialog_frame.sizePolicy().hasHeightForWidth()) | ||||
|         self.dialog_frame.setSizePolicy(sizePolicy) | ||||
|         self.dialog_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.dialog_frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.dialog_frame.setObjectName("dialog_frame") | ||||
|         self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.dialog_frame) | ||||
|         self.verticalLayout_2.setObjectName("verticalLayout_2") | ||||
|         self.title_label = QtWidgets.QLabel(self.dialog_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.title_label.sizePolicy().hasHeightForWidth()) | ||||
|         self.title_label.setSizePolicy(sizePolicy) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(10) | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.title_label.setFont(font) | ||||
|         self.title_label.setLayoutDirection(QtCore.Qt.LeftToRight) | ||||
|         self.title_label.setAlignment(QtCore.Qt.AlignCenter) | ||||
|         self.title_label.setObjectName("title_label") | ||||
|         self.verticalLayout_2.addWidget(self.title_label) | ||||
|         self.host_label = QtWidgets.QLabel(self.dialog_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.host_label.sizePolicy().hasHeightForWidth()) | ||||
|         self.host_label.setSizePolicy(sizePolicy) | ||||
|         font = QtGui.QFont() | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.host_label.setFont(font) | ||||
|         self.host_label.setObjectName("host_label") | ||||
|         self.verticalLayout_2.addWidget(self.host_label) | ||||
|         self.host_line_edit = QtWidgets.QLineEdit(self.dialog_frame) | ||||
|         self.host_line_edit.setObjectName("host_line_edit") | ||||
|         self.verticalLayout_2.addWidget(self.host_line_edit) | ||||
|         self.port_label = QtWidgets.QLabel(self.dialog_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.port_label.sizePolicy().hasHeightForWidth()) | ||||
|         self.port_label.setSizePolicy(sizePolicy) | ||||
|         font = QtGui.QFont() | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.port_label.setFont(font) | ||||
|         self.port_label.setObjectName("port_label") | ||||
|         self.verticalLayout_2.addWidget(self.port_label) | ||||
|         self.port_line_edit = QtWidgets.QLineEdit(self.dialog_frame) | ||||
|         self.port_line_edit.setObjectName("port_line_edit") | ||||
|         self.verticalLayout_2.addWidget(self.port_line_edit) | ||||
|         self.database_label = QtWidgets.QLabel(self.dialog_frame) | ||||
|         font = QtGui.QFont() | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.database_label.setFont(font) | ||||
|         self.database_label.setObjectName("database_label") | ||||
|         self.verticalLayout_2.addWidget(self.database_label) | ||||
|         self.database_line_edit = QtWidgets.QLineEdit(self.dialog_frame) | ||||
|         self.database_line_edit.setObjectName("database_line_edit") | ||||
|         self.verticalLayout_2.addWidget(self.database_line_edit) | ||||
|         self.user_label = QtWidgets.QLabel(self.dialog_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.user_label.sizePolicy().hasHeightForWidth()) | ||||
|         self.user_label.setSizePolicy(sizePolicy) | ||||
|         font = QtGui.QFont() | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.user_label.setFont(font) | ||||
|         self.user_label.setObjectName("user_label") | ||||
|         self.verticalLayout_2.addWidget(self.user_label) | ||||
|         self.user_line_edit = QtWidgets.QLineEdit(self.dialog_frame) | ||||
|         self.user_line_edit.setObjectName("user_line_edit") | ||||
|         self.verticalLayout_2.addWidget(self.user_line_edit) | ||||
|         self.password_label = QtWidgets.QLabel(self.dialog_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.password_label.sizePolicy().hasHeightForWidth()) | ||||
|         self.password_label.setSizePolicy(sizePolicy) | ||||
|         font = QtGui.QFont() | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.password_label.setFont(font) | ||||
|         self.password_label.setObjectName("password_label") | ||||
|         self.verticalLayout_2.addWidget(self.password_label) | ||||
|         self.password_line_edit = QtWidgets.QLineEdit(self.dialog_frame) | ||||
|         self.password_line_edit.setEchoMode(QtWidgets.QLineEdit.Password) | ||||
|         self.password_line_edit.setObjectName("password_line_edit") | ||||
|         self.verticalLayout_2.addWidget(self.password_line_edit) | ||||
|         self.verticalLayout.addWidget(self.dialog_frame) | ||||
|         self.buttonBox = QtWidgets.QDialogButtonBox(DBConnection) | ||||
|         self.buttonBox.setOrientation(QtCore.Qt.Horizontal) | ||||
|         self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) | ||||
|         self.buttonBox.setObjectName("buttonBox") | ||||
|         self.verticalLayout.addWidget(self.buttonBox) | ||||
| 
 | ||||
|         self.retranslateUi(DBConnection) | ||||
|         self.buttonBox.accepted.connect(DBConnection.accept) | ||||
|         self.buttonBox.rejected.connect(DBConnection.reject) | ||||
|         QtCore.QMetaObject.connectSlotsByName(DBConnection) | ||||
| 
 | ||||
|     def retranslateUi(self, DBConnection): | ||||
|         _translate = QtCore.QCoreApplication.translate | ||||
|         DBConnection.setWindowTitle(_translate("DBConnection", "Dialog")) | ||||
|         self.title_label.setText(_translate("DBConnection", "Edit PostgreSQL Connection")) | ||||
|         self.host_label.setText(_translate("DBConnection", "Host:")) | ||||
|         self.port_label.setText(_translate("DBConnection", "Port:")) | ||||
|         self.database_label.setText(_translate("DBConnection", "Database:")) | ||||
|         self.user_label.setText(_translate("DBConnection", "User:")) | ||||
|         self.password_label.setText(_translate("DBConnection", "Password:")) | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     import sys | ||||
|     app = QtWidgets.QApplication(sys.argv) | ||||
|     DBConnection = QtWidgets.QDialog() | ||||
|     ui = Ui_DBConnection() | ||||
|     ui.setupUi(DBConnection) | ||||
|     DBConnection.show() | ||||
|     sys.exit(app.exec_()) | ||||
| @ -0,0 +1,216 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>DBConnection</class> | ||||
|  <widget class="QDialog" name="DBConnection"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>328</width> | ||||
|     <height>262</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Dialog</string> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|    <item> | ||||
|     <widget class="QFrame" name="dialog_frame"> | ||||
|      <property name="sizePolicy"> | ||||
|       <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|        <horstretch>0</horstretch> | ||||
|        <verstretch>0</verstretch> | ||||
|       </sizepolicy> | ||||
|      </property> | ||||
|      <property name="frameShape"> | ||||
|       <enum>QFrame::StyledPanel</enum> | ||||
|      </property> | ||||
|      <property name="frameShadow"> | ||||
|       <enum>QFrame::Raised</enum> | ||||
|      </property> | ||||
|      <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="title_label"> | ||||
|         <property name="sizePolicy"> | ||||
|          <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|           <horstretch>0</horstretch> | ||||
|           <verstretch>0</verstretch> | ||||
|          </sizepolicy> | ||||
|         </property> | ||||
|         <property name="font"> | ||||
|          <font> | ||||
|           <pointsize>10</pointsize> | ||||
|           <weight>75</weight> | ||||
|           <bold>true</bold> | ||||
|          </font> | ||||
|         </property> | ||||
|         <property name="layoutDirection"> | ||||
|          <enum>Qt::LeftToRight</enum> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Edit PostgreSQL Connection</string> | ||||
|         </property> | ||||
|         <property name="alignment"> | ||||
|          <set>Qt::AlignCenter</set> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="host_label"> | ||||
|         <property name="sizePolicy"> | ||||
|          <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|           <horstretch>0</horstretch> | ||||
|           <verstretch>0</verstretch> | ||||
|          </sizepolicy> | ||||
|         </property> | ||||
|         <property name="font"> | ||||
|          <font> | ||||
|           <weight>75</weight> | ||||
|           <bold>true</bold> | ||||
|          </font> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Host:</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLineEdit" name="host_line_edit"/> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="port_label"> | ||||
|         <property name="sizePolicy"> | ||||
|          <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|           <horstretch>0</horstretch> | ||||
|           <verstretch>0</verstretch> | ||||
|          </sizepolicy> | ||||
|         </property> | ||||
|         <property name="font"> | ||||
|          <font> | ||||
|           <weight>75</weight> | ||||
|           <bold>true</bold> | ||||
|          </font> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Port:</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLineEdit" name="port_line_edit"/> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="database_label"> | ||||
|         <property name="font"> | ||||
|          <font> | ||||
|           <weight>75</weight> | ||||
|           <bold>true</bold> | ||||
|          </font> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Database:</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLineEdit" name="database_line_edit"/> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="user_label"> | ||||
|         <property name="sizePolicy"> | ||||
|          <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|           <horstretch>0</horstretch> | ||||
|           <verstretch>0</verstretch> | ||||
|          </sizepolicy> | ||||
|         </property> | ||||
|         <property name="font"> | ||||
|          <font> | ||||
|           <weight>75</weight> | ||||
|           <bold>true</bold> | ||||
|          </font> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>User:</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLineEdit" name="user_line_edit"/> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="password_label"> | ||||
|         <property name="sizePolicy"> | ||||
|          <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|           <horstretch>0</horstretch> | ||||
|           <verstretch>0</verstretch> | ||||
|          </sizepolicy> | ||||
|         </property> | ||||
|         <property name="font"> | ||||
|          <font> | ||||
|           <weight>75</weight> | ||||
|           <bold>true</bold> | ||||
|          </font> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Password:</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLineEdit" name="password_line_edit"> | ||||
|         <property name="echoMode"> | ||||
|          <enum>QLineEdit::Password</enum> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QDialogButtonBox" name="buttonBox"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Horizontal</enum> | ||||
|      </property> | ||||
|      <property name="standardButtons"> | ||||
|       <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>accepted()</signal> | ||||
|    <receiver>DBConnection</receiver> | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>248</x> | ||||
|      <y>254</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>157</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>rejected()</signal> | ||||
|    <receiver>DBConnection</receiver> | ||||
|    <slot>reject()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>316</x> | ||||
|      <y>260</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>286</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|  </connections> | ||||
| </ui> | ||||
| @ -0,0 +1,31 @@ | ||||
| from PyQt5 import QtWidgets | ||||
| 
 | ||||
| from ArtNet.gui.dialogs.db_connection_dialog.db_connection_dialog import Ui_DBConnection | ||||
| 
 | ||||
| 
 | ||||
| class DBDialog(QtWidgets.QDialog): | ||||
| 
 | ||||
|     def __init__(self, parent=None): | ||||
|         super().__init__(parent) | ||||
|         self.ui = Ui_DBConnection() | ||||
|         self.ui.setupUi(self) | ||||
|         self.data = {} | ||||
| 
 | ||||
|         self.ui.buttonBox.accepted.connect(self.getConnectionDetails) | ||||
| 
 | ||||
|     def getConnectionDetails(self): | ||||
|         self.data = dict() | ||||
| 
 | ||||
|         self.data["host"] = self.ui.host_line_edit.text() | ||||
|         self.data["port"] = self.ui.port_line_edit.text() | ||||
|         self.data["database"] = self.ui.database_line_edit.text() | ||||
|         self.data["user"] = self.ui.user_line_edit.text() | ||||
|         self.data["password"] = self.ui.password_line_edit.text() | ||||
| 
 | ||||
|         self.accept() | ||||
| 
 | ||||
|     def exec_(self) -> dict: | ||||
|         if super(DBDialog, self).exec_() == QtWidgets.QDialog.Rejected: | ||||
|             return None | ||||
|         return self.data | ||||
| 
 | ||||
| @ -0,0 +1,200 @@ | ||||
| from PyQt5 import QtWidgets | ||||
| from PyQt5.QtCore import Qt | ||||
| from PyQt5.QtGui import QStandardItem, QStandardItemModel | ||||
| 
 | ||||
| from ArtNet.gui.dialogs.presence_modify_dialog.presence_modify_dialog import Ui_PresenceModDialog | ||||
| from ArtNet.gui.dialogs.artist_modify_dialog.artist_mod_dialog import ArtistModDialog | ||||
| 
 | ||||
| 
 | ||||
| class PresenceModDialog(QtWidgets.QDialog): | ||||
| 
 | ||||
|     def __init__(self, parent=None, edit_presence: bool = False): | ||||
|         super().__init__(parent) | ||||
|         self.parent = parent | ||||
|         self.ui = Ui_PresenceModDialog() | ||||
|         self.ui.setupUi(self) | ||||
| 
 | ||||
|         self.curr_artist: tuple = None | ||||
|         self.__curr_searched_artists: list = [] | ||||
| 
 | ||||
|         self.data: dict = None | ||||
| 
 | ||||
|         if edit_presence: | ||||
|             self.ui.dialog_topic_label.setText("Edit Presence") | ||||
|             self.ui.presence_domain_line.setReadOnly(True) | ||||
|             self.ui.presence_name_line.setReadOnly(True) | ||||
|         else: | ||||
|             self.ui.dialog_topic_label.setText("Create Presence") | ||||
|             self.ui.presence_domain_line.setReadOnly(False) | ||||
|             self.ui.presence_name_line.setReadOnly(False) | ||||
| 
 | ||||
|         self.ui.artist_search_line.textChanged.connect(self.on_artist_search_changed) | ||||
| 
 | ||||
|         self.ui.create_artist_button.clicked.connect(self.on_create_artist_clicked) | ||||
|         self.ui.edit_artist_button.clicked.connect(self.on_edit_artist_clicked) | ||||
|         self.ui.delete_artist_button.clicked.connect(self.on_delete_artist_clicked) | ||||
| 
 | ||||
|     def set_presence_name(self, name: str): | ||||
|         self.ui.presence_name_line.setText(name) | ||||
| 
 | ||||
|     def set_presence_domain(self, name: str): | ||||
|         self.ui.presence_domain_line.setText(name) | ||||
| 
 | ||||
|     def on_create_artist_clicked(self): | ||||
|         print("Clicked Create artist!") | ||||
|         dialog = ArtistModDialog(self) | ||||
| 
 | ||||
|         data = dialog.exec_() | ||||
|         if data is None: | ||||
|             return | ||||
| 
 | ||||
|         self.parent.parent.create_artist(ID=data["id"], description=data["description"]) | ||||
| 
 | ||||
|     def on_edit_artist_clicked(self): | ||||
|         print("Clicked Edit artist!") | ||||
| 
 | ||||
|     def on_delete_artist_clicked(self): | ||||
|         print("Clicked Delete artist!") | ||||
| 
 | ||||
|         if self.curr_artist is None: | ||||
|             QtWidgets.QMessageBox.information(self, "No Artist Selected", "There is no artist selected to delete!") | ||||
|             return | ||||
| 
 | ||||
|         answer = QtWidgets.QMessageBox.question(self, "Delete this Artist?", | ||||
|                                                 "Do you really want to delete the Artist \"{0}\"?" | ||||
|                                                 .format(self.curr_artist[1])) | ||||
|         if answer == QtWidgets.QMessageBox.Yes: | ||||
|             pass  # TODO check if presences are associated & warn | ||||
|             presences = self.parent.parent.get_artist_presences(self.curr_artist[0]) | ||||
| 
 | ||||
|             msg = "" | ||||
|             affected_art = 0 | ||||
|             for presence in presences: | ||||
|                 msg += presence[0]+":"+presence[1] + "\n" | ||||
| 
 | ||||
|                 arts = self.parent.parent.get_presences_art(presence[0], presence[1]) | ||||
|                 affected_art += len(arts) | ||||
| 
 | ||||
|             answer = QtWidgets.QMessageBox.question(self, "Are you sure?", | ||||
|                                                     ("Do you really wish to delete the Artist \"{0}\"?\n" + | ||||
|                                                      "following Presences are associated with " + | ||||
|                                                      "it and deleted also:\n\n" + | ||||
|                                                      "{1}\n" + | ||||
|                                                      "This will also remove this presence from following " + | ||||
|                                                      "amount of art pieces:" + | ||||
|                                                      "\n{2}\n") | ||||
|                                                     .format(self.curr_artist[1], msg, affected_art) | ||||
|                                                     ) | ||||
|             if answer == QtWidgets.QMessageBox.Yes: | ||||
|                 pass | ||||
|                  | ||||
|                 self.parent.parent.remove_artist(self.curr_artist[0]) | ||||
|                 for presence in presences: | ||||
|                     self.parent.parent.remove_presence(presence[0], presence[1]) | ||||
|                 self.close() | ||||
| 
 | ||||
|             else: | ||||
|                 return | ||||
|         else: | ||||
|             return | ||||
| 
 | ||||
|     def on_artist_search_changed(self): | ||||
|         if len(self.ui.artist_search_line.text()) == 0:  # nothing to search for | ||||
|             self.set_artist_result_list([]) | ||||
|             return | ||||
|         artists = self.parent.parent.get_artists(self.ui.artist_search_line.text()) | ||||
| 
 | ||||
|         result = [] | ||||
|         for ID, desc in artists: | ||||
|             result.append(str(ID)+":"+desc) | ||||
| 
 | ||||
|         self.set_artist_result_list(result) | ||||
| 
 | ||||
|     def set_artist_result_list(self, artists: list): | ||||
|         """ | ||||
|         Set the list of search result artists. | ||||
|         :param artists: [(id, desc)] | ||||
|         :return: | ||||
|         """ | ||||
|         item_model = QStandardItemModel(self.ui.artist_result_list) | ||||
| 
 | ||||
|         self.__curr_searched_artists = [] | ||||
|         for artist in artists: | ||||
|             self.__curr_searched_artists.append(artist) | ||||
| 
 | ||||
|             item = QStandardItem(artist) | ||||
|             item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) | ||||
|             item.setData(Qt.Unchecked, Qt.CheckStateRole) | ||||
| 
 | ||||
|             if self.curr_artist is not None: | ||||
|                 curr_artist = str(self.curr_artist[0]) + ":" + self.curr_artist[1] | ||||
|                 if artist == curr_artist: | ||||
|                     item.setCheckState(Qt.Checked) | ||||
| 
 | ||||
|             item_model.appendRow(item) | ||||
| 
 | ||||
|         item_model.itemChanged.connect(self.on_search_artist_item_changed) | ||||
|         self.ui.artist_result_list.setModel(item_model) | ||||
| 
 | ||||
|     def set_artist_selected_list(self, artist: tuple): | ||||
|         """ | ||||
|         Set the list of selected artist | ||||
|         :param artists: [(id, desc)] | ||||
|         :return: | ||||
|         """ | ||||
|         if artist is None: | ||||
|             return | ||||
| 
 | ||||
|         item_model = QStandardItemModel(self.ui.artist_selection_list) | ||||
| 
 | ||||
|         item = QStandardItem(str(artist[0])+":"+artist[1]) | ||||
|         item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) | ||||
|         item.setData(Qt.Unchecked, Qt.CheckStateRole) | ||||
|         item.setCheckState(Qt.Checked) | ||||
| 
 | ||||
|         item_model.appendRow(item) | ||||
| 
 | ||||
|         item_model.itemChanged.connect(self.on_selected_artist_item_changed) | ||||
|         self.ui.artist_selection_list.setModel(item_model) | ||||
| 
 | ||||
|     def on_search_artist_item_changed(self, item: QStandardItem): | ||||
|         artist = item.text().split(":") | ||||
|         artist = (artist[0], artist[1]) | ||||
|         if item.checkState() == Qt.Checked: | ||||
|             self.curr_artist = artist | ||||
|         elif item.checkState() == Qt.Unchecked: | ||||
|             self.curr_artist = None | ||||
|         self.set_artist_result_list(self.__curr_searched_artists) | ||||
|         self.set_artist_selected_list(self.curr_artist) | ||||
| 
 | ||||
|     def on_selected_artist_item_changed(self, item: QStandardItem): | ||||
|         artist = item.text().split(":") | ||||
|         artist = (artist[0].strip(), artist[1].strip()) | ||||
|         if item.checkState() == Qt.Checked: | ||||
|             self.curr_artist = artist | ||||
|         elif item.checkState() == Qt.Unchecked: | ||||
|             self.curr_artist = artist | ||||
|         self.set_artist_result_list(self.__curr_searched_artists) | ||||
|         self.set_artist_selected_list(self.curr_artist) | ||||
| 
 | ||||
|     def collect_presence_details(self): | ||||
|         self.data = { | ||||
|             "name": self.ui.presence_name_line.text(), | ||||
|             "domain": self.ui.presence_domain_line.text(), | ||||
|             "artist": self.curr_artist, | ||||
|             "link": self.ui.presence_link_list.text(), | ||||
|         } | ||||
| 
 | ||||
|         is_null = True | ||||
|         for value in self.data.values(): | ||||
|             if value is not None and len(value) != 0: | ||||
|                 is_null = False | ||||
|         if is_null: | ||||
|             self.data = None | ||||
| 
 | ||||
|     def exec_(self) -> dict: | ||||
|         if super(PresenceModDialog, self).exec_() == QtWidgets.QDialog.Rejected: | ||||
|             return None | ||||
|         self.collect_presence_details() | ||||
| 
 | ||||
|         return self.data | ||||
| @ -0,0 +1,149 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| 
 | ||||
| # Form implementation generated from reading ui file 'dialogs/presence_modify_dialog/presence_modify_dialog.ui' | ||||
| # | ||||
| # Created by: PyQt5 UI code generator 5.15.0 | ||||
| # | ||||
| # WARNING: Any manual changes made to this file will be lost when pyuic5 is | ||||
| # run again.  Do not edit this file unless you know what you are doing. | ||||
| 
 | ||||
| 
 | ||||
| from PyQt5 import QtCore, QtGui, QtWidgets | ||||
| 
 | ||||
| 
 | ||||
| class Ui_PresenceModDialog(object): | ||||
|     def setupUi(self, PresenceModDialog): | ||||
|         PresenceModDialog.setObjectName("PresenceModDialog") | ||||
|         PresenceModDialog.resize(376, 405) | ||||
|         self.verticalLayout = QtWidgets.QVBoxLayout(PresenceModDialog) | ||||
|         self.verticalLayout.setObjectName("verticalLayout") | ||||
|         self.frame = QtWidgets.QFrame(PresenceModDialog) | ||||
|         self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.frame.setObjectName("frame") | ||||
|         self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.frame) | ||||
|         self.verticalLayout_2.setObjectName("verticalLayout_2") | ||||
|         self.dialog_topic_label = QtWidgets.QLabel(self.frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.dialog_topic_label.sizePolicy().hasHeightForWidth()) | ||||
|         self.dialog_topic_label.setSizePolicy(sizePolicy) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(11) | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.dialog_topic_label.setFont(font) | ||||
|         self.dialog_topic_label.setAlignment(QtCore.Qt.AlignCenter) | ||||
|         self.dialog_topic_label.setObjectName("dialog_topic_label") | ||||
|         self.verticalLayout_2.addWidget(self.dialog_topic_label) | ||||
|         self.frame_2 = QtWidgets.QFrame(self.frame) | ||||
|         self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.frame_2.setObjectName("frame_2") | ||||
|         self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.frame_2) | ||||
|         self.verticalLayout_3.setSpacing(5) | ||||
|         self.verticalLayout_3.setObjectName("verticalLayout_3") | ||||
|         self.presence_name_label = QtWidgets.QLabel(self.frame_2) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.presence_name_label.sizePolicy().hasHeightForWidth()) | ||||
|         self.presence_name_label.setSizePolicy(sizePolicy) | ||||
|         font = QtGui.QFont() | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.presence_name_label.setFont(font) | ||||
|         self.presence_name_label.setObjectName("presence_name_label") | ||||
|         self.verticalLayout_3.addWidget(self.presence_name_label) | ||||
|         self.presence_name_line = QtWidgets.QLineEdit(self.frame_2) | ||||
|         self.presence_name_line.setObjectName("presence_name_line") | ||||
|         self.verticalLayout_3.addWidget(self.presence_name_line) | ||||
|         self.presence_domain_label = QtWidgets.QLabel(self.frame_2) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.presence_domain_label.sizePolicy().hasHeightForWidth()) | ||||
|         self.presence_domain_label.setSizePolicy(sizePolicy) | ||||
|         font = QtGui.QFont() | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.presence_domain_label.setFont(font) | ||||
|         self.presence_domain_label.setObjectName("presence_domain_label") | ||||
|         self.verticalLayout_3.addWidget(self.presence_domain_label) | ||||
|         self.presence_domain_line = QtWidgets.QLineEdit(self.frame_2) | ||||
|         self.presence_domain_line.setObjectName("presence_domain_line") | ||||
|         self.verticalLayout_3.addWidget(self.presence_domain_line) | ||||
|         self.presence_link_label = QtWidgets.QLabel(self.frame_2) | ||||
|         font = QtGui.QFont() | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.presence_link_label.setFont(font) | ||||
|         self.presence_link_label.setObjectName("presence_link_label") | ||||
|         self.verticalLayout_3.addWidget(self.presence_link_label) | ||||
|         self.presence_link_list = QtWidgets.QLineEdit(self.frame_2) | ||||
|         self.presence_link_list.setObjectName("presence_link_list") | ||||
|         self.verticalLayout_3.addWidget(self.presence_link_list) | ||||
|         self.artist_label = QtWidgets.QLabel(self.frame_2) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(10) | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.artist_label.setFont(font) | ||||
|         self.artist_label.setObjectName("artist_label") | ||||
|         self.verticalLayout_3.addWidget(self.artist_label) | ||||
|         self.artist_search_line = QtWidgets.QLineEdit(self.frame_2) | ||||
|         self.artist_search_line.setObjectName("artist_search_line") | ||||
|         self.verticalLayout_3.addWidget(self.artist_search_line) | ||||
|         self.artist_result_list = QtWidgets.QListView(self.frame_2) | ||||
|         self.artist_result_list.setObjectName("artist_result_list") | ||||
|         self.verticalLayout_3.addWidget(self.artist_result_list) | ||||
|         self.artist_selection_list = QtWidgets.QListView(self.frame_2) | ||||
|         self.artist_selection_list.setObjectName("artist_selection_list") | ||||
|         self.verticalLayout_3.addWidget(self.artist_selection_list) | ||||
|         self.artist_edit_layout = QtWidgets.QHBoxLayout() | ||||
|         self.artist_edit_layout.setObjectName("artist_edit_layout") | ||||
|         self.create_artist_button = QtWidgets.QPushButton(self.frame_2) | ||||
|         self.create_artist_button.setObjectName("create_artist_button") | ||||
|         self.artist_edit_layout.addWidget(self.create_artist_button) | ||||
|         self.edit_artist_button = QtWidgets.QPushButton(self.frame_2) | ||||
|         self.edit_artist_button.setObjectName("edit_artist_button") | ||||
|         self.artist_edit_layout.addWidget(self.edit_artist_button) | ||||
|         self.delete_artist_button = QtWidgets.QPushButton(self.frame_2) | ||||
|         self.delete_artist_button.setObjectName("delete_artist_button") | ||||
|         self.artist_edit_layout.addWidget(self.delete_artist_button) | ||||
|         self.verticalLayout_3.addLayout(self.artist_edit_layout) | ||||
|         self.verticalLayout_2.addWidget(self.frame_2) | ||||
|         self.verticalLayout.addWidget(self.frame) | ||||
|         self.buttonBox = QtWidgets.QDialogButtonBox(PresenceModDialog) | ||||
|         self.buttonBox.setOrientation(QtCore.Qt.Horizontal) | ||||
|         self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) | ||||
|         self.buttonBox.setObjectName("buttonBox") | ||||
|         self.verticalLayout.addWidget(self.buttonBox) | ||||
| 
 | ||||
|         self.retranslateUi(PresenceModDialog) | ||||
|         self.buttonBox.accepted.connect(PresenceModDialog.accept) | ||||
|         self.buttonBox.rejected.connect(PresenceModDialog.reject) | ||||
|         QtCore.QMetaObject.connectSlotsByName(PresenceModDialog) | ||||
| 
 | ||||
|     def retranslateUi(self, PresenceModDialog): | ||||
|         _translate = QtCore.QCoreApplication.translate | ||||
|         PresenceModDialog.setWindowTitle(_translate("PresenceModDialog", "Dialog")) | ||||
|         self.dialog_topic_label.setText(_translate("PresenceModDialog", "Presence Topic")) | ||||
|         self.presence_name_label.setText(_translate("PresenceModDialog", "Presence Name:")) | ||||
|         self.presence_domain_label.setText(_translate("PresenceModDialog", "Presence Domain:")) | ||||
|         self.presence_link_label.setText(_translate("PresenceModDialog", "Presence Link:")) | ||||
|         self.artist_label.setText(_translate("PresenceModDialog", "Artist:")) | ||||
|         self.create_artist_button.setText(_translate("PresenceModDialog", "Create Artist")) | ||||
|         self.edit_artist_button.setText(_translate("PresenceModDialog", "Edit Artist")) | ||||
|         self.delete_artist_button.setText(_translate("PresenceModDialog", "Delete Artist")) | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     import sys | ||||
|     app = QtWidgets.QApplication(sys.argv) | ||||
|     PresenceModDialog = QtWidgets.QDialog() | ||||
|     ui = Ui_PresenceModDialog() | ||||
|     ui.setupUi(PresenceModDialog) | ||||
|     PresenceModDialog.show() | ||||
|     sys.exit(app.exec_()) | ||||
| @ -0,0 +1,222 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>PresenceModDialog</class> | ||||
|  <widget class="QDialog" name="PresenceModDialog"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>376</width> | ||||
|     <height>405</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Dialog</string> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|    <item> | ||||
|     <widget class="QFrame" name="frame"> | ||||
|      <property name="frameShape"> | ||||
|       <enum>QFrame::StyledPanel</enum> | ||||
|      </property> | ||||
|      <property name="frameShadow"> | ||||
|       <enum>QFrame::Raised</enum> | ||||
|      </property> | ||||
|      <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="dialog_topic_label"> | ||||
|         <property name="sizePolicy"> | ||||
|          <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|           <horstretch>0</horstretch> | ||||
|           <verstretch>0</verstretch> | ||||
|          </sizepolicy> | ||||
|         </property> | ||||
|         <property name="font"> | ||||
|          <font> | ||||
|           <pointsize>11</pointsize> | ||||
|           <weight>75</weight> | ||||
|           <bold>true</bold> | ||||
|          </font> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Presence Topic</string> | ||||
|         </property> | ||||
|         <property name="alignment"> | ||||
|          <set>Qt::AlignCenter</set> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QFrame" name="frame_2"> | ||||
|         <property name="frameShape"> | ||||
|          <enum>QFrame::StyledPanel</enum> | ||||
|         </property> | ||||
|         <property name="frameShadow"> | ||||
|          <enum>QFrame::Raised</enum> | ||||
|         </property> | ||||
|         <layout class="QVBoxLayout" name="verticalLayout_3"> | ||||
|          <property name="spacing"> | ||||
|           <number>5</number> | ||||
|          </property> | ||||
|          <item> | ||||
|           <widget class="QLabel" name="presence_name_label"> | ||||
|            <property name="sizePolicy"> | ||||
|             <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|              <horstretch>0</horstretch> | ||||
|              <verstretch>0</verstretch> | ||||
|             </sizepolicy> | ||||
|            </property> | ||||
|            <property name="font"> | ||||
|             <font> | ||||
|              <weight>75</weight> | ||||
|              <bold>true</bold> | ||||
|             </font> | ||||
|            </property> | ||||
|            <property name="text"> | ||||
|             <string>Presence Name:</string> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QLineEdit" name="presence_name_line"/> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QLabel" name="presence_domain_label"> | ||||
|            <property name="sizePolicy"> | ||||
|             <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|              <horstretch>0</horstretch> | ||||
|              <verstretch>0</verstretch> | ||||
|             </sizepolicy> | ||||
|            </property> | ||||
|            <property name="font"> | ||||
|             <font> | ||||
|              <weight>75</weight> | ||||
|              <bold>true</bold> | ||||
|             </font> | ||||
|            </property> | ||||
|            <property name="text"> | ||||
|             <string>Presence Domain:</string> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QLineEdit" name="presence_domain_line"/> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QLabel" name="presence_link_label"> | ||||
|            <property name="font"> | ||||
|             <font> | ||||
|              <weight>75</weight> | ||||
|              <bold>true</bold> | ||||
|             </font> | ||||
|            </property> | ||||
|            <property name="text"> | ||||
|             <string>Presence Link:</string> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QLineEdit" name="presence_link_list"/> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QLabel" name="artist_label"> | ||||
|            <property name="font"> | ||||
|             <font> | ||||
|              <pointsize>10</pointsize> | ||||
|              <weight>75</weight> | ||||
|              <bold>true</bold> | ||||
|             </font> | ||||
|            </property> | ||||
|            <property name="text"> | ||||
|             <string>Artist:</string> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QLineEdit" name="artist_search_line"/> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QListView" name="artist_result_list"/> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QListView" name="artist_selection_list"/> | ||||
|          </item> | ||||
|          <item> | ||||
|           <layout class="QHBoxLayout" name="artist_edit_layout"> | ||||
|            <item> | ||||
|             <widget class="QPushButton" name="create_artist_button"> | ||||
|              <property name="text"> | ||||
|               <string>Create Artist</string> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QPushButton" name="edit_artist_button"> | ||||
|              <property name="text"> | ||||
|               <string>Edit Artist</string> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QPushButton" name="delete_artist_button"> | ||||
|              <property name="text"> | ||||
|               <string>Delete Artist</string> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|           </layout> | ||||
|          </item> | ||||
|         </layout> | ||||
|        </widget> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QDialogButtonBox" name="buttonBox"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Horizontal</enum> | ||||
|      </property> | ||||
|      <property name="standardButtons"> | ||||
|       <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>accepted()</signal> | ||||
|    <receiver>PresenceModDialog</receiver> | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>248</x> | ||||
|      <y>254</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>157</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>rejected()</signal> | ||||
|    <receiver>PresenceModDialog</receiver> | ||||
|    <slot>reject()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>316</x> | ||||
|      <y>260</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>286</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|  </connections> | ||||
| </ui> | ||||
| @ -0,0 +1,70 @@ | ||||
| from PyQt5 import QtWidgets | ||||
| from PyQt5.QtCore import Qt | ||||
| from PyQt5.QtGui import QStandardItem, QStandardItemModel | ||||
| 
 | ||||
| from ArtNet.gui.dialogs.presence_selection_dialog.presence_selection_dialog import Ui_presence_selection_dialog | ||||
| 
 | ||||
| 
 | ||||
| class PresenceSelectDialog(QtWidgets.QDialog): | ||||
| 
 | ||||
|     def __init__(self, parent=None): | ||||
|         super().__init__(parent) | ||||
|         self.parent = parent | ||||
|         self.ui = Ui_presence_selection_dialog() | ||||
|         self.ui.setupUi(self) | ||||
| 
 | ||||
|         self.__curr_presence: list = None | ||||
|         self.__curr_search_result: list = None | ||||
| 
 | ||||
|         self.ui.presence_name_search_line.textChanged.connect(self.on_search_change) | ||||
|         self.ui.presence_domain_search_line.textChanged.connect(self.on_search_change) | ||||
| 
 | ||||
|     def set_search_result_list(self, result: list): | ||||
|         self.__curr_search_result = result | ||||
|         item_model = QStandardItemModel(self.ui.presence_search_list) | ||||
| 
 | ||||
|         for presence in result: | ||||
|             item = QStandardItem(presence) | ||||
|             item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) | ||||
|             item.setData(Qt.Unchecked, Qt.CheckStateRole) | ||||
| 
 | ||||
|             if self.__curr_presence is not None: | ||||
|                 curr_presence = self.__curr_presence[0] + ":" + self.__curr_presence[1] | ||||
|                 if presence == curr_presence: | ||||
|                     item.setCheckState(Qt.Checked) | ||||
| 
 | ||||
|             item_model.appendRow(item) | ||||
| 
 | ||||
|         item_model.itemChanged.connect(self.on_search_presence_item_changed) | ||||
|         self.ui.presence_search_list.setModel(item_model) | ||||
| 
 | ||||
|     def on_search_change(self): | ||||
|         name = self.ui.presence_name_search_line.text() | ||||
|         domain = self.ui.presence_domain_search_line.text() | ||||
| 
 | ||||
|         if len(name) == 0 and len(domain) == 0:  # nothing to search for | ||||
|             self.set_search_result_list([]) | ||||
|             return | ||||
|         presences = self.parent.parent.get_authors(name, domain) | ||||
| 
 | ||||
|         result = [] | ||||
|         for ID, desc in presences: | ||||
|             result.append(ID+":"+desc) | ||||
| 
 | ||||
|         self.set_search_result_list(result) | ||||
| 
 | ||||
|     def on_search_presence_item_changed(self, item: QStandardItem): | ||||
|         name, domain = item.text().split(":") | ||||
|         if item.checkState() == Qt.Checked: | ||||
|             self.__curr_presence = self.parent.parent.get_presence(name, domain) | ||||
|             if self.__curr_presence is not None: | ||||
|                 self.__curr_presence = self.__curr_presence[0]  # unpack from list | ||||
|         elif item.checkState() == Qt.Unchecked: | ||||
|             self.__curr_presence = None | ||||
|         self.set_search_result_list(self.__curr_search_result) | ||||
| 
 | ||||
|     def exec_(self) -> list: | ||||
|         if super(PresenceSelectDialog, self).exec_() == QtWidgets.QDialog.Rejected: | ||||
|             return None | ||||
| 
 | ||||
|         return self.__curr_presence | ||||
| @ -0,0 +1,101 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| 
 | ||||
| # Form implementation generated from reading ui file 'dialogs/presence_selection_dialog/presence_selection_dialog.ui' | ||||
| # | ||||
| # Created by: PyQt5 UI code generator 5.15.0 | ||||
| # | ||||
| # WARNING: Any manual changes made to this file will be lost when pyuic5 is | ||||
| # run again.  Do not edit this file unless you know what you are doing. | ||||
| 
 | ||||
| 
 | ||||
| from PyQt5 import QtCore, QtGui, QtWidgets | ||||
| 
 | ||||
| 
 | ||||
| class Ui_presence_selection_dialog(object): | ||||
|     def setupUi(self, presence_selection_dialog): | ||||
|         presence_selection_dialog.setObjectName("presence_selection_dialog") | ||||
|         presence_selection_dialog.resize(330, 260) | ||||
|         self.verticalLayout = QtWidgets.QVBoxLayout(presence_selection_dialog) | ||||
|         self.verticalLayout.setObjectName("verticalLayout") | ||||
|         self.frame = QtWidgets.QFrame(presence_selection_dialog) | ||||
|         self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.frame.setObjectName("frame") | ||||
|         self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.frame) | ||||
|         self.verticalLayout_2.setObjectName("verticalLayout_2") | ||||
|         self.label = QtWidgets.QLabel(self.frame) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(11) | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.label.setFont(font) | ||||
|         self.label.setAlignment(QtCore.Qt.AlignCenter) | ||||
|         self.label.setObjectName("label") | ||||
|         self.verticalLayout_2.addWidget(self.label) | ||||
|         self.frame_2 = QtWidgets.QFrame(self.frame) | ||||
|         self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.frame_2.setObjectName("frame_2") | ||||
|         self.horizontalLayout = QtWidgets.QHBoxLayout(self.frame_2) | ||||
|         self.horizontalLayout.setObjectName("horizontalLayout") | ||||
|         self.verticalLayout_4 = QtWidgets.QVBoxLayout() | ||||
|         self.verticalLayout_4.setObjectName("verticalLayout_4") | ||||
|         self.label_2 = QtWidgets.QLabel(self.frame_2) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(10) | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.label_2.setFont(font) | ||||
|         self.label_2.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) | ||||
|         self.label_2.setObjectName("label_2") | ||||
|         self.verticalLayout_4.addWidget(self.label_2) | ||||
|         self.presence_name_search_line = QtWidgets.QLineEdit(self.frame_2) | ||||
|         self.presence_name_search_line.setObjectName("presence_name_search_line") | ||||
|         self.verticalLayout_4.addWidget(self.presence_name_search_line) | ||||
|         self.horizontalLayout.addLayout(self.verticalLayout_4) | ||||
|         self.verticalLayout_3 = QtWidgets.QVBoxLayout() | ||||
|         self.verticalLayout_3.setObjectName("verticalLayout_3") | ||||
|         self.label_3 = QtWidgets.QLabel(self.frame_2) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(10) | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.label_3.setFont(font) | ||||
|         self.label_3.setObjectName("label_3") | ||||
|         self.verticalLayout_3.addWidget(self.label_3) | ||||
|         self.presence_domain_search_line = QtWidgets.QLineEdit(self.frame_2) | ||||
|         self.presence_domain_search_line.setObjectName("presence_domain_search_line") | ||||
|         self.verticalLayout_3.addWidget(self.presence_domain_search_line) | ||||
|         self.horizontalLayout.addLayout(self.verticalLayout_3) | ||||
|         self.verticalLayout_2.addWidget(self.frame_2) | ||||
|         self.presence_search_list = QtWidgets.QListView(self.frame) | ||||
|         self.presence_search_list.setObjectName("presence_search_list") | ||||
|         self.verticalLayout_2.addWidget(self.presence_search_list) | ||||
|         self.verticalLayout.addWidget(self.frame) | ||||
|         self.buttonBox = QtWidgets.QDialogButtonBox(presence_selection_dialog) | ||||
|         self.buttonBox.setOrientation(QtCore.Qt.Horizontal) | ||||
|         self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) | ||||
|         self.buttonBox.setObjectName("buttonBox") | ||||
|         self.verticalLayout.addWidget(self.buttonBox) | ||||
| 
 | ||||
|         self.retranslateUi(presence_selection_dialog) | ||||
|         self.buttonBox.accepted.connect(presence_selection_dialog.accept) | ||||
|         self.buttonBox.rejected.connect(presence_selection_dialog.reject) | ||||
|         QtCore.QMetaObject.connectSlotsByName(presence_selection_dialog) | ||||
| 
 | ||||
|     def retranslateUi(self, presence_selection_dialog): | ||||
|         _translate = QtCore.QCoreApplication.translate | ||||
|         presence_selection_dialog.setWindowTitle(_translate("presence_selection_dialog", "Dialog")) | ||||
|         self.label.setText(_translate("presence_selection_dialog", "Select Presence")) | ||||
|         self.label_2.setText(_translate("presence_selection_dialog", "Name:")) | ||||
|         self.label_3.setText(_translate("presence_selection_dialog", "Domain:")) | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     import sys | ||||
|     app = QtWidgets.QApplication(sys.argv) | ||||
|     presence_selection_dialog = QtWidgets.QDialog() | ||||
|     ui = Ui_presence_selection_dialog() | ||||
|     ui.setupUi(presence_selection_dialog) | ||||
|     presence_selection_dialog.show() | ||||
|     sys.exit(app.exec_()) | ||||
| @ -0,0 +1,153 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>presence_selection_dialog</class> | ||||
|  <widget class="QDialog" name="presence_selection_dialog"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>330</width> | ||||
|     <height>260</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Dialog</string> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|    <item> | ||||
|     <widget class="QFrame" name="frame"> | ||||
|      <property name="frameShape"> | ||||
|       <enum>QFrame::StyledPanel</enum> | ||||
|      </property> | ||||
|      <property name="frameShadow"> | ||||
|       <enum>QFrame::Raised</enum> | ||||
|      </property> | ||||
|      <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="label"> | ||||
|         <property name="font"> | ||||
|          <font> | ||||
|           <pointsize>11</pointsize> | ||||
|           <weight>75</weight> | ||||
|           <bold>true</bold> | ||||
|          </font> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Select Presence</string> | ||||
|         </property> | ||||
|         <property name="alignment"> | ||||
|          <set>Qt::AlignCenter</set> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QFrame" name="frame_2"> | ||||
|         <property name="frameShape"> | ||||
|          <enum>QFrame::StyledPanel</enum> | ||||
|         </property> | ||||
|         <property name="frameShadow"> | ||||
|          <enum>QFrame::Raised</enum> | ||||
|         </property> | ||||
|         <layout class="QHBoxLayout" name="horizontalLayout"> | ||||
|          <item> | ||||
|           <layout class="QVBoxLayout" name="verticalLayout_4"> | ||||
|            <item> | ||||
|             <widget class="QLabel" name="label_2"> | ||||
|              <property name="font"> | ||||
|               <font> | ||||
|                <pointsize>10</pointsize> | ||||
|                <weight>75</weight> | ||||
|                <bold>true</bold> | ||||
|               </font> | ||||
|              </property> | ||||
|              <property name="text"> | ||||
|               <string>Name:</string> | ||||
|              </property> | ||||
|              <property name="alignment"> | ||||
|               <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QLineEdit" name="presence_name_search_line"/> | ||||
|            </item> | ||||
|           </layout> | ||||
|          </item> | ||||
|          <item> | ||||
|           <layout class="QVBoxLayout" name="verticalLayout_3"> | ||||
|            <item> | ||||
|             <widget class="QLabel" name="label_3"> | ||||
|              <property name="font"> | ||||
|               <font> | ||||
|                <pointsize>10</pointsize> | ||||
|                <weight>75</weight> | ||||
|                <bold>true</bold> | ||||
|               </font> | ||||
|              </property> | ||||
|              <property name="text"> | ||||
|               <string>Domain:</string> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QLineEdit" name="presence_domain_search_line"/> | ||||
|            </item> | ||||
|           </layout> | ||||
|          </item> | ||||
|         </layout> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QListView" name="presence_search_list"/> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QDialogButtonBox" name="buttonBox"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Horizontal</enum> | ||||
|      </property> | ||||
|      <property name="standardButtons"> | ||||
|       <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>accepted()</signal> | ||||
|    <receiver>presence_selection_dialog</receiver> | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>248</x> | ||||
|      <y>254</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>157</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>rejected()</signal> | ||||
|    <receiver>presence_selection_dialog</receiver> | ||||
|    <slot>reject()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>316</x> | ||||
|      <y>260</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>286</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|  </connections> | ||||
| </ui> | ||||
| @ -0,0 +1,122 @@ | ||||
| from PyQt5 import QtWidgets | ||||
| from PyQt5.QtGui import QStandardItemModel, QStandardItem | ||||
| from PyQt5.QtCore import Qt | ||||
| 
 | ||||
| from ArtNet.gui.dialogs.tag_import_dialog.tag_import_dialog import Ui_Dialog | ||||
| 
 | ||||
| 
 | ||||
| class TagImportDialog(QtWidgets.QDialog): | ||||
| 
 | ||||
|     def __init__(self, parent=None): | ||||
|         super().__init__(parent=parent) | ||||
|         self.parent = parent | ||||
|         self.ui = Ui_Dialog() | ||||
|         self.ui.setupUi(self) | ||||
|         self.ui.import_all_button.clicked.connect(self.on_import_all_clicked) | ||||
|         self.ui.remove_all_button.clicked.connect(self.on_remove_all_clicked) | ||||
| 
 | ||||
|         self.to_import = [] | ||||
|         self.not_import = [] | ||||
| 
 | ||||
|         self.data = dict() | ||||
| 
 | ||||
|     def set_import_tag_list(self, tags: list): | ||||
|         """ | ||||
|         Set the tags in the imported list | ||||
|         :param tags: | ||||
|         :return: | ||||
|         """ | ||||
|         set_checked = True | ||||
|         item_model = QStandardItemModel(self.ui.import_list) | ||||
|         self.data = tags | ||||
| 
 | ||||
|         for tag in tags: | ||||
|             item = QStandardItem(tag) | ||||
|             item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) | ||||
|             item.setData(Qt.Unchecked, Qt.CheckStateRole) | ||||
|             if set_checked: | ||||
|                 item.setCheckState(Qt.Checked) | ||||
| 
 | ||||
|             item_model.appendRow(item) | ||||
| 
 | ||||
|         item_model.itemChanged.connect(self.on_import_item_changed) | ||||
|         self.ui.import_list.setModel(item_model) | ||||
| 
 | ||||
|     def set_detected_artists(self, artists: list): | ||||
|         """ | ||||
|         Set the label for detected artists | ||||
|         :param artists: | ||||
|         :return: | ||||
|         """ | ||||
|         s = "" | ||||
|         for artist in artists: | ||||
|             s += artist + " | " | ||||
|         s = s[:-3] | ||||
| 
 | ||||
|         self.ui.detected_artists_label.setText(s) | ||||
| 
 | ||||
|     def set_used_link(self, link: str): | ||||
|         """ | ||||
|         Set the label to display the predicted link | ||||
|         :param link: | ||||
|         :return: | ||||
|         """ | ||||
|         self.ui.used_link_label.setText(link) | ||||
| 
 | ||||
|     def set_ignore_tag_list(self, tags: list): | ||||
|         """ | ||||
|         Set the tags in the ignore list | ||||
|         :param tags: | ||||
|         :return: | ||||
|         """ | ||||
|         set_checked = False | ||||
|         item_model = QStandardItemModel(self.ui.ignored_list) | ||||
| 
 | ||||
|         for tag in tags: | ||||
|             item = QStandardItem(tag) | ||||
|             item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) | ||||
|             item.setData(Qt.Unchecked, Qt.CheckStateRole) | ||||
|             if set_checked: | ||||
|                 item.setCheckState(Qt.Checked) | ||||
| 
 | ||||
|             item_model.appendRow(item) | ||||
| 
 | ||||
|         item_model.itemChanged.connect(self.on_ignore_item_changed) | ||||
|         self.ui.ignored_list.setModel(item_model) | ||||
| 
 | ||||
|     def on_ignore_item_changed(self, item: QStandardItem): | ||||
|         if item.checkState() == Qt.Checked: | ||||
|             tag = item.text() | ||||
|             self.to_import.append(tag) | ||||
|             self.not_import.remove(tag) | ||||
| 
 | ||||
|         self.set_import_tag_list(self.to_import) | ||||
|         self.set_ignore_tag_list(self.not_import) | ||||
| 
 | ||||
|     def on_import_item_changed(self, item: QStandardItem): | ||||
|         if item.checkState() == Qt.Unchecked: | ||||
|             tag = item.text() | ||||
|             self.to_import.remove(tag) | ||||
|             self.not_import.append(tag) | ||||
| 
 | ||||
|         self.set_import_tag_list(self.to_import) | ||||
|         self.set_ignore_tag_list(self.not_import) | ||||
| 
 | ||||
|     def on_import_all_clicked(self): | ||||
|         self.to_import += self.not_import | ||||
|         self.not_import = [] | ||||
| 
 | ||||
|         self.set_import_tag_list(self.to_import) | ||||
|         self.set_ignore_tag_list(self.not_import) | ||||
| 
 | ||||
|     def on_remove_all_clicked(self): | ||||
|         self.not_import += self.to_import | ||||
|         self.to_import = [] | ||||
| 
 | ||||
|         self.set_import_tag_list(self.to_import) | ||||
|         self.set_ignore_tag_list(self.not_import) | ||||
| 
 | ||||
|     def exec_(self) -> int: | ||||
|         if super(TagImportDialog, self).exec_() == QtWidgets.QDialog.Rejected: | ||||
|             return None | ||||
|         return self.data | ||||
| @ -0,0 +1,143 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| 
 | ||||
| # Form implementation generated from reading ui file 'dialogs/tag_import_dialog/tag_import_dialog.ui' | ||||
| # | ||||
| # Created by: PyQt5 UI code generator 5.15.0 | ||||
| # | ||||
| # WARNING: Any manual changes made to this file will be lost when pyuic5 is | ||||
| # run again.  Do not edit this file unless you know what you are doing. | ||||
| 
 | ||||
| 
 | ||||
| from PyQt5 import QtCore, QtGui, QtWidgets | ||||
| 
 | ||||
| 
 | ||||
| class Ui_Dialog(object): | ||||
|     def setupUi(self, Dialog): | ||||
|         Dialog.setObjectName("Dialog") | ||||
|         Dialog.resize(775, 300) | ||||
|         self.verticalLayout = QtWidgets.QVBoxLayout(Dialog) | ||||
|         self.verticalLayout.setObjectName("verticalLayout") | ||||
|         self.label = QtWidgets.QLabel(Dialog) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth()) | ||||
|         self.label.setSizePolicy(sizePolicy) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(12) | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.label.setFont(font) | ||||
|         self.label.setAlignment(QtCore.Qt.AlignCenter) | ||||
|         self.label.setObjectName("label") | ||||
|         self.verticalLayout.addWidget(self.label) | ||||
|         self.frame_4 = QtWidgets.QFrame(Dialog) | ||||
|         self.frame_4.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.frame_4.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.frame_4.setObjectName("frame_4") | ||||
|         self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.frame_4) | ||||
|         self.verticalLayout_4.setObjectName("verticalLayout_4") | ||||
|         self.label_4 = QtWidgets.QLabel(self.frame_4) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(9) | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.label_4.setFont(font) | ||||
|         self.label_4.setAlignment(QtCore.Qt.AlignCenter) | ||||
|         self.label_4.setObjectName("label_4") | ||||
|         self.verticalLayout_4.addWidget(self.label_4) | ||||
|         self.detected_artists_label = QtWidgets.QLabel(self.frame_4) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(8) | ||||
|         self.detected_artists_label.setFont(font) | ||||
|         self.detected_artists_label.setAlignment(QtCore.Qt.AlignCenter) | ||||
|         self.detected_artists_label.setObjectName("detected_artists_label") | ||||
|         self.verticalLayout_4.addWidget(self.detected_artists_label) | ||||
|         self.used_link_label = QtWidgets.QLabel(self.frame_4) | ||||
|         self.used_link_label.setAlignment(QtCore.Qt.AlignCenter) | ||||
|         self.used_link_label.setObjectName("used_link_label") | ||||
|         self.verticalLayout_4.addWidget(self.used_link_label) | ||||
|         self.verticalLayout.addWidget(self.frame_4) | ||||
|         self.frame = QtWidgets.QFrame(Dialog) | ||||
|         self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.frame.setObjectName("frame") | ||||
|         self.horizontalLayout = QtWidgets.QHBoxLayout(self.frame) | ||||
|         self.horizontalLayout.setObjectName("horizontalLayout") | ||||
|         self.frame_2 = QtWidgets.QFrame(self.frame) | ||||
|         self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.frame_2.setObjectName("frame_2") | ||||
|         self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.frame_2) | ||||
|         self.verticalLayout_3.setObjectName("verticalLayout_3") | ||||
|         self.label_2 = QtWidgets.QLabel(self.frame_2) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(10) | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.label_2.setFont(font) | ||||
|         self.label_2.setObjectName("label_2") | ||||
|         self.verticalLayout_3.addWidget(self.label_2) | ||||
|         self.import_list = QtWidgets.QListView(self.frame_2) | ||||
|         self.import_list.setMinimumSize(QtCore.QSize(300, 0)) | ||||
|         self.import_list.setObjectName("import_list") | ||||
|         self.verticalLayout_3.addWidget(self.import_list) | ||||
|         self.remove_all_button = QtWidgets.QPushButton(self.frame_2) | ||||
|         self.remove_all_button.setObjectName("remove_all_button") | ||||
|         self.verticalLayout_3.addWidget(self.remove_all_button) | ||||
|         self.horizontalLayout.addWidget(self.frame_2) | ||||
|         self.frame_3 = QtWidgets.QFrame(self.frame) | ||||
|         self.frame_3.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.frame_3.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.frame_3.setObjectName("frame_3") | ||||
|         self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.frame_3) | ||||
|         self.verticalLayout_2.setObjectName("verticalLayout_2") | ||||
|         self.label_3 = QtWidgets.QLabel(self.frame_3) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(10) | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.label_3.setFont(font) | ||||
|         self.label_3.setObjectName("label_3") | ||||
|         self.verticalLayout_2.addWidget(self.label_3) | ||||
|         self.ignored_list = QtWidgets.QListView(self.frame_3) | ||||
|         self.ignored_list.setMinimumSize(QtCore.QSize(300, 0)) | ||||
|         self.ignored_list.setObjectName("ignored_list") | ||||
|         self.verticalLayout_2.addWidget(self.ignored_list) | ||||
|         self.import_all_button = QtWidgets.QPushButton(self.frame_3) | ||||
|         self.import_all_button.setObjectName("import_all_button") | ||||
|         self.verticalLayout_2.addWidget(self.import_all_button) | ||||
|         self.horizontalLayout.addWidget(self.frame_3) | ||||
|         self.verticalLayout.addWidget(self.frame) | ||||
|         self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) | ||||
|         self.buttonBox.setOrientation(QtCore.Qt.Horizontal) | ||||
|         self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) | ||||
|         self.buttonBox.setObjectName("buttonBox") | ||||
|         self.verticalLayout.addWidget(self.buttonBox) | ||||
| 
 | ||||
|         self.retranslateUi(Dialog) | ||||
|         self.buttonBox.accepted.connect(Dialog.accept) | ||||
|         self.buttonBox.rejected.connect(Dialog.reject) | ||||
|         QtCore.QMetaObject.connectSlotsByName(Dialog) | ||||
| 
 | ||||
|     def retranslateUi(self, Dialog): | ||||
|         _translate = QtCore.QCoreApplication.translate | ||||
|         Dialog.setWindowTitle(_translate("Dialog", "Dialog")) | ||||
|         self.label.setText(_translate("Dialog", "Detected Tags")) | ||||
|         self.label_4.setText(_translate("Dialog", "Detected Artists")) | ||||
|         self.detected_artists_label.setText(_translate("Dialog", "(None)")) | ||||
|         self.used_link_label.setText(_translate("Dialog", "-No Link Used-")) | ||||
|         self.label_2.setText(_translate("Dialog", "To be Imported:")) | ||||
|         self.remove_all_button.setText(_translate("Dialog", "Remove All")) | ||||
|         self.label_3.setText(_translate("Dialog", "To Be Ignored:")) | ||||
|         self.import_all_button.setText(_translate("Dialog", "Import All")) | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     import sys | ||||
|     app = QtWidgets.QApplication(sys.argv) | ||||
|     Dialog = QtWidgets.QDialog() | ||||
|     ui = Ui_Dialog() | ||||
|     ui.setupUi(Dialog) | ||||
|     Dialog.show() | ||||
|     sys.exit(app.exec_()) | ||||
| @ -0,0 +1,239 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>Dialog</class> | ||||
|  <widget class="QDialog" name="Dialog"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>775</width> | ||||
|     <height>300</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Dialog</string> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|    <item> | ||||
|     <widget class="QLabel" name="label"> | ||||
|      <property name="sizePolicy"> | ||||
|       <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|        <horstretch>0</horstretch> | ||||
|        <verstretch>0</verstretch> | ||||
|       </sizepolicy> | ||||
|      </property> | ||||
|      <property name="font"> | ||||
|       <font> | ||||
|        <pointsize>12</pointsize> | ||||
|        <weight>75</weight> | ||||
|        <bold>true</bold> | ||||
|       </font> | ||||
|      </property> | ||||
|      <property name="text"> | ||||
|       <string>Detected Tags</string> | ||||
|      </property> | ||||
|      <property name="alignment"> | ||||
|       <set>Qt::AlignCenter</set> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QFrame" name="frame_4"> | ||||
|      <property name="frameShape"> | ||||
|       <enum>QFrame::StyledPanel</enum> | ||||
|      </property> | ||||
|      <property name="frameShadow"> | ||||
|       <enum>QFrame::Raised</enum> | ||||
|      </property> | ||||
|      <layout class="QVBoxLayout" name="verticalLayout_4"> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="label_4"> | ||||
|         <property name="font"> | ||||
|          <font> | ||||
|           <pointsize>9</pointsize> | ||||
|           <weight>75</weight> | ||||
|           <bold>true</bold> | ||||
|          </font> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Detected Artists</string> | ||||
|         </property> | ||||
|         <property name="alignment"> | ||||
|          <set>Qt::AlignCenter</set> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="detected_artists_label"> | ||||
|         <property name="font"> | ||||
|          <font> | ||||
|           <pointsize>8</pointsize> | ||||
|          </font> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>(None)</string> | ||||
|         </property> | ||||
|         <property name="alignment"> | ||||
|          <set>Qt::AlignCenter</set> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="used_link_label"> | ||||
|         <property name="text"> | ||||
|          <string>-No Link Used-</string> | ||||
|         </property> | ||||
|         <property name="alignment"> | ||||
|          <set>Qt::AlignCenter</set> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QFrame" name="frame"> | ||||
|      <property name="frameShape"> | ||||
|       <enum>QFrame::StyledPanel</enum> | ||||
|      </property> | ||||
|      <property name="frameShadow"> | ||||
|       <enum>QFrame::Raised</enum> | ||||
|      </property> | ||||
|      <layout class="QHBoxLayout" name="horizontalLayout"> | ||||
|       <item> | ||||
|        <widget class="QFrame" name="frame_2"> | ||||
|         <property name="frameShape"> | ||||
|          <enum>QFrame::StyledPanel</enum> | ||||
|         </property> | ||||
|         <property name="frameShadow"> | ||||
|          <enum>QFrame::Raised</enum> | ||||
|         </property> | ||||
|         <layout class="QVBoxLayout" name="verticalLayout_3"> | ||||
|          <item> | ||||
|           <widget class="QLabel" name="label_2"> | ||||
|            <property name="font"> | ||||
|             <font> | ||||
|              <pointsize>10</pointsize> | ||||
|              <weight>75</weight> | ||||
|              <bold>true</bold> | ||||
|             </font> | ||||
|            </property> | ||||
|            <property name="text"> | ||||
|             <string>To be Imported:</string> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QListView" name="import_list"> | ||||
|            <property name="minimumSize"> | ||||
|             <size> | ||||
|              <width>300</width> | ||||
|              <height>0</height> | ||||
|             </size> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QPushButton" name="remove_all_button"> | ||||
|            <property name="text"> | ||||
|             <string>Remove All</string> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|         </layout> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QFrame" name="frame_3"> | ||||
|         <property name="frameShape"> | ||||
|          <enum>QFrame::StyledPanel</enum> | ||||
|         </property> | ||||
|         <property name="frameShadow"> | ||||
|          <enum>QFrame::Raised</enum> | ||||
|         </property> | ||||
|         <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||
|          <item> | ||||
|           <widget class="QLabel" name="label_3"> | ||||
|            <property name="font"> | ||||
|             <font> | ||||
|              <pointsize>10</pointsize> | ||||
|              <weight>75</weight> | ||||
|              <bold>true</bold> | ||||
|             </font> | ||||
|            </property> | ||||
|            <property name="text"> | ||||
|             <string>To Be Ignored:</string> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QListView" name="ignored_list"> | ||||
|            <property name="minimumSize"> | ||||
|             <size> | ||||
|              <width>300</width> | ||||
|              <height>0</height> | ||||
|             </size> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QPushButton" name="import_all_button"> | ||||
|            <property name="text"> | ||||
|             <string>Import All</string> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|         </layout> | ||||
|        </widget> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QDialogButtonBox" name="buttonBox"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Horizontal</enum> | ||||
|      </property> | ||||
|      <property name="standardButtons"> | ||||
|       <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>accepted()</signal> | ||||
|    <receiver>Dialog</receiver> | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>248</x> | ||||
|      <y>254</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>157</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>rejected()</signal> | ||||
|    <receiver>Dialog</receiver> | ||||
|    <slot>reject()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>316</x> | ||||
|      <y>260</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>286</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|  </connections> | ||||
| </ui> | ||||
| @ -0,0 +1,285 @@ | ||||
| from PyQt5 import QtWidgets | ||||
| from PyQt5.QtGui import QStandardItemModel, QStandardItem | ||||
| from PyQt5.QtCore import Qt | ||||
| 
 | ||||
| from ArtNet.gui.dialogs.tag_modify_dialog.tag_modify_dialog import Ui_TagModify | ||||
| 
 | ||||
| 
 | ||||
| class TagModifyDialog(QtWidgets.QDialog): | ||||
| 
 | ||||
|     def __init__(self, parent=None, create_tag: bool = True): | ||||
|         super().__init__(parent) | ||||
|         self.parent = parent | ||||
|         self.ui = Ui_TagModify() | ||||
|         self.ui.setupUi(self) | ||||
| 
 | ||||
|         self.data = dict() | ||||
|         self.original_tag_name: str = None | ||||
|         self.create_tag = create_tag | ||||
| 
 | ||||
|         self.alias_selection: list = None | ||||
|         self.implication_selection: list = None | ||||
|         self.tag_alias_search_result: list = None | ||||
|         self.tag_implication_search_result: list = None | ||||
|         self.category_selection: str = None | ||||
| 
 | ||||
|         if create_tag: | ||||
|             self.ui.dialog_topic.setText("Create Tag") | ||||
|         else: | ||||
|             self.ui.dialog_topic.setText("Edit Tag") | ||||
|             self.ui.tag_name_line.setReadOnly(False) | ||||
| 
 | ||||
|         self.ui.buttonBox.accepted.connect(self.getTagDetails) | ||||
| 
 | ||||
|         self.ui.tag_alias_search_line.textChanged.connect(self.on_tag_alias_search_line_changed) | ||||
|         self.ui.tag_implication_search_line.textChanged.connect(self.on_tag_implication_search_line_changed) | ||||
|         self.set_all_categories() | ||||
| 
 | ||||
|     def set_search_alias_tags(self, aliases: list, set_checked: bool = False): | ||||
|         """ | ||||
|         Set the tags in the search result list to tags | ||||
|         :param aliases: | ||||
|         :param set_checked: | ||||
|         :return: | ||||
|         """ | ||||
|         if aliases is None: | ||||
|             return | ||||
|         item_model = QStandardItemModel(self.ui.tag_alias_search_list) | ||||
| 
 | ||||
|         for tag in aliases: | ||||
|             tag = tag[0].strip() | ||||
|             if tag == self.ui.tag_name_line.text(): | ||||
|                 continue | ||||
|             item = QStandardItem(tag) | ||||
|             item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) | ||||
|             item.setData(Qt.Unchecked, Qt.CheckStateRole) | ||||
| 
 | ||||
|             if set_checked: | ||||
|                 item.setCheckState(Qt.Checked) | ||||
| 
 | ||||
|             item_model.appendRow(item) | ||||
| 
 | ||||
|         item_model.itemChanged.connect(self.on_search_alias_tag_item_changed) | ||||
|         self.ui.tag_alias_search_list.setModel(item_model) | ||||
| 
 | ||||
|     def set_search_implicated_tags(self, implications: list, set_checked: bool=False): | ||||
|         """ | ||||
|         Set the tags in the search result list to tags | ||||
|         :param implications: | ||||
|         :param set_checked: | ||||
|         :return: | ||||
|         """ | ||||
|         item_model = QStandardItemModel(self.ui.tag_implication_search_list) | ||||
| 
 | ||||
|         for tag in implications: | ||||
|             tag = tag[0].strip() | ||||
|             if tag == self.ui.tag_name_line.text(): | ||||
|                 continue | ||||
|             item = QStandardItem(tag) | ||||
|             item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) | ||||
|             item.setData(Qt.Unchecked, Qt.CheckStateRole) | ||||
|             if set_checked: | ||||
|                 item.setCheckState(Qt.Checked) | ||||
| 
 | ||||
|             item_model.appendRow(item) | ||||
| 
 | ||||
|         item_model.itemChanged.connect(self.on_search_implicated_tag_item_changed) | ||||
|         self.ui.tag_implication_search_list.setModel(item_model) | ||||
| 
 | ||||
|     def set_all_categories(self): | ||||
|         """ | ||||
|         Populate the categories list and only check the one selection | ||||
|         :return: | ||||
|         """ | ||||
|         categories = self.parent.get_categories("", all_if_empty=True) | ||||
|         item_model = QStandardItemModel(self.ui.category_list) | ||||
| 
 | ||||
|         for category in categories: | ||||
|             category = category.strip() | ||||
| 
 | ||||
|             item = QStandardItem(category) | ||||
|             item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) | ||||
|             item.setData(Qt.Unchecked, Qt.CheckStateRole) | ||||
| 
 | ||||
|             if category == self.category_selection: | ||||
|                 item.setCheckState(Qt.Checked) | ||||
| 
 | ||||
|             item_model.appendRow(item) | ||||
| 
 | ||||
|         item_model.itemChanged.connect(self.on_category_item_changed) | ||||
|         self.ui.category_list.setModel(item_model) | ||||
| 
 | ||||
|     def set_selected_alias_tags(self, aliases: list, set_checked: bool=False): | ||||
|         """ | ||||
|         Set the tags in the search result list to tags | ||||
|         :param aliases: | ||||
|         :param set_checked: | ||||
|         :return: | ||||
|         """ | ||||
|         item_model = QStandardItemModel(self.ui.tag_alias_selection_list) | ||||
| 
 | ||||
|         for tag in aliases: | ||||
|             tag = tag.strip() | ||||
|             if tag == self.ui.tag_name_line.text(): | ||||
|                 continue | ||||
|             item = QStandardItem(tag) | ||||
|             item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) | ||||
|             item.setData(Qt.Unchecked, Qt.CheckStateRole) | ||||
| 
 | ||||
|             if set_checked: | ||||
|                 item.setCheckState(Qt.Checked) | ||||
| 
 | ||||
|             item_model.appendRow(item) | ||||
| 
 | ||||
|         item_model.itemChanged.connect(self.on_selected_alias_tag_item_changed) | ||||
|         self.ui.tag_alias_selection_list.setModel(item_model) | ||||
| 
 | ||||
|     def set_selected_implicated_tags(self, implications: list, set_checked: bool=False): | ||||
|         """ | ||||
|         Set the tags in the search result list to tags | ||||
|         :param implications: | ||||
|         :param set_checked: | ||||
|         :return: | ||||
|         """ | ||||
|         item_model = QStandardItemModel(self.ui.tag_implication_search_list) | ||||
| 
 | ||||
|         for tag in implications: | ||||
|             tag = tag.strip() | ||||
|             if tag == self.ui.tag_name_line.text(): | ||||
|                 continue | ||||
|             item = QStandardItem(tag) | ||||
|             item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) | ||||
|             item.setData(Qt.Unchecked, Qt.CheckStateRole) | ||||
|             if set_checked: | ||||
|                 item.setCheckState(Qt.Checked) | ||||
| 
 | ||||
|             item_model.appendRow(item) | ||||
| 
 | ||||
|         item_model.itemChanged.connect(self.on_selected_implicated_tag_item_changed) | ||||
|         self.ui.tag_implication_selection_list.setModel(item_model) | ||||
| 
 | ||||
|     def on_tag_alias_search_line_changed(self): | ||||
|         search_text = self.ui.tag_alias_search_line.text() | ||||
|         if len(search_text) == 0: | ||||
|             tags = [] | ||||
|         else: | ||||
|             tags = self.parent.get_tag_search_result(search_text) | ||||
| 
 | ||||
|         if tags is None: | ||||
|             return | ||||
| 
 | ||||
|         self.tag_alias_search_result = tags | ||||
|         self.set_search_alias_tags(tags) | ||||
| 
 | ||||
|     def on_tag_implication_search_line_changed(self): | ||||
|         search_text = self.ui.tag_implication_search_line.text() | ||||
|         if len(search_text) == 0: | ||||
|             tags = [] | ||||
|         else: | ||||
|             tags = self.parent.get_tag_search_result(search_text) | ||||
| 
 | ||||
|         if tags is None: | ||||
|             return | ||||
|         self.tag_implication_search_result = tags | ||||
|         self.set_search_implicated_tags(tags) | ||||
| 
 | ||||
|     def on_search_implicated_tag_item_changed(self, item: QStandardItem): | ||||
|         if item.checkState() == Qt.Checked: | ||||
|             if self.implication_selection is None: | ||||
|                 self.implication_selection = [] | ||||
|             self.implication_selection.append(item.text()) | ||||
|         elif item.checkState() == Qt.Unchecked: | ||||
|             if item.text() in self.implication_selection: | ||||
|                 self.implication_selection.remove(item.text()) | ||||
| 
 | ||||
|         self.set_selected_implicated_tags(self.implication_selection, set_checked=True) | ||||
| 
 | ||||
|     def on_search_alias_tag_item_changed(self, item: QStandardItem): | ||||
|         if item.checkState() == Qt.Checked: | ||||
|             if self.alias_selection is None: | ||||
|                 self.alias_selection = [] | ||||
|             self.alias_selection.append(item.text()) | ||||
|         elif item.checkState() == Qt.Unchecked: | ||||
|             if item.text() in self.alias_selection: | ||||
|                 self.alias_selection.remove(item.text()) | ||||
| 
 | ||||
|         self.set_selected_alias_tags(self.alias_selection, set_checked=True) | ||||
| 
 | ||||
|     def on_selected_implicated_tag_item_changed(self, item: QStandardItem): | ||||
|         if self.implication_selection is None: | ||||
|             self.implication_selection = [] | ||||
|         self.implication_selection.remove(item.text()) | ||||
|         self.set_selected_implicated_tags(self.implication_selection, set_checked=True) | ||||
| 
 | ||||
|         if self.tag_implication_search_result is not None: | ||||
|             self.set_search_implicated_tags(self.tag_implication_search_result) | ||||
| 
 | ||||
|     def on_selected_alias_tag_item_changed(self, item: QStandardItem): | ||||
|         if self.alias_selection is None: | ||||
|             self.alias_selection = [] | ||||
|         self.alias_selection.remove(item.text()) | ||||
|         self.set_selected_alias_tags(self.alias_selection, set_checked=True) | ||||
| 
 | ||||
|         self.set_search_alias_tags(self.tag_alias_search_result) | ||||
| 
 | ||||
|     def on_category_item_changed(self, item: QStandardItem): | ||||
|         if self.category_selection is None: | ||||
|             self.category_selection = [] | ||||
| 
 | ||||
|         if item.checkState() == Qt.Checked: | ||||
|             self.category_selection = item.text() | ||||
|         elif item.checkState() == Qt.Unchecked: | ||||
|             self.category_selection = None | ||||
| 
 | ||||
|         self.set_all_categories() | ||||
| 
 | ||||
|     def has_critical_info(self) -> bool: | ||||
|         """ | ||||
|         Checks if all required fields are filled | ||||
|         :return: | ||||
|         """ | ||||
|         ok = True | ||||
|         if len(self.ui.tag_name_line.text()) == 0: | ||||
|             ok = False | ||||
|         if self.category_selection is None: | ||||
|             ok = False | ||||
| 
 | ||||
|         if not ok: | ||||
|             msg = QtWidgets.QMessageBox() | ||||
|             msg.setWindowTitle("Missing Fields") | ||||
|             msg.setIcon(QtWidgets.QMessageBox.Critical) | ||||
|             msg.setInformativeText( | ||||
|                 "Either the tag name or the category selection is empty! Can't save the tag this way!") | ||||
| 
 | ||||
|             msg.exec_() | ||||
|         return ok | ||||
| 
 | ||||
|     def getTagDetails(self): | ||||
|         self.data = dict() | ||||
| 
 | ||||
|         self.data["name"] = self.ui.tag_name_line.text() | ||||
|         self.data["description"] = self.ui.tag_description_area.toPlainText() | ||||
| 
 | ||||
|         self.data["aliases"] = self.alias_selection | ||||
|         self.data["implications"] = self.implication_selection | ||||
| 
 | ||||
|         self.data["category"] = self.category_selection | ||||
| 
 | ||||
|         if not self.has_critical_info(): | ||||
|             self.data = None | ||||
|             self.reject() | ||||
|             return | ||||
| 
 | ||||
|         self.accept() | ||||
| 
 | ||||
|     def exec_(self) -> dict: | ||||
|         self.original_tag_name = self.ui.tag_name_line.text() | ||||
| 
 | ||||
|         if super(TagModifyDialog, self).exec_() == QtWidgets.QDialog.Rejected: | ||||
|             return None | ||||
| 
 | ||||
|         if not self.create_tag: | ||||
|             if self.data["name"] != self.original_tag_name: | ||||
|                 self.data["old_tag_name"] = self.original_tag_name | ||||
| 
 | ||||
|         return self.data | ||||
| @ -0,0 +1,226 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| 
 | ||||
| # Form implementation generated from reading ui file 'dialogs/tag_modify_dialog/tag_modify_dialog.ui' | ||||
| # | ||||
| # Created by: PyQt5 UI code generator 5.15.0 | ||||
| # | ||||
| # WARNING: Any manual changes made to this file will be lost when pyuic5 is | ||||
| # run again.  Do not edit this file unless you know what you are doing. | ||||
| 
 | ||||
| 
 | ||||
| from PyQt5 import QtCore, QtGui, QtWidgets | ||||
| 
 | ||||
| 
 | ||||
| class Ui_TagModify(object): | ||||
|     def setupUi(self, TagModify): | ||||
|         TagModify.setObjectName("TagModify") | ||||
|         TagModify.resize(587, 846) | ||||
|         self.verticalLayout = QtWidgets.QVBoxLayout(TagModify) | ||||
|         self.verticalLayout.setObjectName("verticalLayout") | ||||
|         self.dialog_frame = QtWidgets.QFrame(TagModify) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.dialog_frame.sizePolicy().hasHeightForWidth()) | ||||
|         self.dialog_frame.setSizePolicy(sizePolicy) | ||||
|         self.dialog_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.dialog_frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.dialog_frame.setObjectName("dialog_frame") | ||||
|         self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.dialog_frame) | ||||
|         self.verticalLayout_2.setObjectName("verticalLayout_2") | ||||
|         self.dialog_topic = QtWidgets.QLabel(self.dialog_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.dialog_topic.sizePolicy().hasHeightForWidth()) | ||||
|         self.dialog_topic.setSizePolicy(sizePolicy) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(10) | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.dialog_topic.setFont(font) | ||||
|         self.dialog_topic.setAlignment(QtCore.Qt.AlignCenter) | ||||
|         self.dialog_topic.setObjectName("dialog_topic") | ||||
|         self.verticalLayout_2.addWidget(self.dialog_topic) | ||||
|         self.tag_name_label = QtWidgets.QLabel(self.dialog_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.tag_name_label.sizePolicy().hasHeightForWidth()) | ||||
|         self.tag_name_label.setSizePolicy(sizePolicy) | ||||
|         font = QtGui.QFont() | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.tag_name_label.setFont(font) | ||||
|         self.tag_name_label.setObjectName("tag_name_label") | ||||
|         self.verticalLayout_2.addWidget(self.tag_name_label) | ||||
|         self.tag_name_line = QtWidgets.QLineEdit(self.dialog_frame) | ||||
|         self.tag_name_line.setObjectName("tag_name_line") | ||||
|         self.verticalLayout_2.addWidget(self.tag_name_line) | ||||
|         self.tag_description_label = QtWidgets.QLabel(self.dialog_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.tag_description_label.sizePolicy().hasHeightForWidth()) | ||||
|         self.tag_description_label.setSizePolicy(sizePolicy) | ||||
|         self.tag_description_label.setObjectName("tag_description_label") | ||||
|         self.verticalLayout_2.addWidget(self.tag_description_label) | ||||
|         self.tag_description_area = QtWidgets.QTextEdit(self.dialog_frame) | ||||
|         self.tag_description_area.setMaximumSize(QtCore.QSize(16777215, 100)) | ||||
|         self.tag_description_area.setObjectName("tag_description_area") | ||||
|         self.verticalLayout_2.addWidget(self.tag_description_area) | ||||
|         self.category_label = QtWidgets.QLabel(self.dialog_frame) | ||||
|         self.category_label.setObjectName("category_label") | ||||
|         self.verticalLayout_2.addWidget(self.category_label) | ||||
|         self.category_list = QtWidgets.QListView(self.dialog_frame) | ||||
|         self.category_list.setMaximumSize(QtCore.QSize(16777215, 100)) | ||||
|         self.category_list.setObjectName("category_list") | ||||
|         self.verticalLayout_2.addWidget(self.category_list) | ||||
|         self.tag_alias_frame = QtWidgets.QFrame(self.dialog_frame) | ||||
|         self.tag_alias_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.tag_alias_frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.tag_alias_frame.setObjectName("tag_alias_frame") | ||||
|         self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.tag_alias_frame) | ||||
|         self.verticalLayout_3.setObjectName("verticalLayout_3") | ||||
|         self.tag_alias_label = QtWidgets.QLabel(self.tag_alias_frame) | ||||
|         font = QtGui.QFont() | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.tag_alias_label.setFont(font) | ||||
|         self.tag_alias_label.setAlignment(QtCore.Qt.AlignCenter) | ||||
|         self.tag_alias_label.setObjectName("tag_alias_label") | ||||
|         self.verticalLayout_3.addWidget(self.tag_alias_label) | ||||
|         self.tag_alias_search_line = QtWidgets.QLineEdit(self.tag_alias_frame) | ||||
|         self.tag_alias_search_line.setObjectName("tag_alias_search_line") | ||||
|         self.verticalLayout_3.addWidget(self.tag_alias_search_line) | ||||
|         self.tag_alias_label_frame = QtWidgets.QFrame(self.tag_alias_frame) | ||||
|         self.tag_alias_label_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.tag_alias_label_frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.tag_alias_label_frame.setObjectName("tag_alias_label_frame") | ||||
|         self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.tag_alias_label_frame) | ||||
|         self.horizontalLayout_2.setObjectName("horizontalLayout_2") | ||||
|         self.tag_alias_search_label = QtWidgets.QLabel(self.tag_alias_label_frame) | ||||
|         self.tag_alias_search_label.setObjectName("tag_alias_search_label") | ||||
|         self.horizontalLayout_2.addWidget(self.tag_alias_search_label) | ||||
|         self.tag_alias_selection_label = QtWidgets.QLabel(self.tag_alias_label_frame) | ||||
|         self.tag_alias_selection_label.setObjectName("tag_alias_selection_label") | ||||
|         self.horizontalLayout_2.addWidget(self.tag_alias_selection_label) | ||||
|         self.verticalLayout_3.addWidget(self.tag_alias_label_frame) | ||||
|         self.tag_alias_list_frame = QtWidgets.QFrame(self.tag_alias_frame) | ||||
|         self.tag_alias_list_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.tag_alias_list_frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.tag_alias_list_frame.setObjectName("tag_alias_list_frame") | ||||
|         self.horizontalLayout = QtWidgets.QHBoxLayout(self.tag_alias_list_frame) | ||||
|         self.horizontalLayout.setObjectName("horizontalLayout") | ||||
|         self.tag_alias_search_list = QtWidgets.QListView(self.tag_alias_list_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.tag_alias_search_list.sizePolicy().hasHeightForWidth()) | ||||
|         self.tag_alias_search_list.setSizePolicy(sizePolicy) | ||||
|         self.tag_alias_search_list.setMaximumSize(QtCore.QSize(16777215, 100)) | ||||
|         self.tag_alias_search_list.setObjectName("tag_alias_search_list") | ||||
|         self.horizontalLayout.addWidget(self.tag_alias_search_list) | ||||
|         self.tag_alias_selection_list = QtWidgets.QListView(self.tag_alias_list_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.tag_alias_selection_list.sizePolicy().hasHeightForWidth()) | ||||
|         self.tag_alias_selection_list.setSizePolicy(sizePolicy) | ||||
|         self.tag_alias_selection_list.setMaximumSize(QtCore.QSize(16777215, 100)) | ||||
|         self.tag_alias_selection_list.setObjectName("tag_alias_selection_list") | ||||
|         self.horizontalLayout.addWidget(self.tag_alias_selection_list) | ||||
|         self.verticalLayout_3.addWidget(self.tag_alias_list_frame) | ||||
|         self.verticalLayout_2.addWidget(self.tag_alias_frame) | ||||
|         self.tag_implication_frame = QtWidgets.QFrame(self.dialog_frame) | ||||
|         self.tag_implication_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.tag_implication_frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.tag_implication_frame.setObjectName("tag_implication_frame") | ||||
|         self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.tag_implication_frame) | ||||
|         self.verticalLayout_4.setObjectName("verticalLayout_4") | ||||
|         self.tag_implication_label = QtWidgets.QLabel(self.tag_implication_frame) | ||||
|         font = QtGui.QFont() | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.tag_implication_label.setFont(font) | ||||
|         self.tag_implication_label.setAlignment(QtCore.Qt.AlignCenter) | ||||
|         self.tag_implication_label.setObjectName("tag_implication_label") | ||||
|         self.verticalLayout_4.addWidget(self.tag_implication_label) | ||||
|         self.tag_implication_search_line = QtWidgets.QLineEdit(self.tag_implication_frame) | ||||
|         self.tag_implication_search_line.setObjectName("tag_implication_search_line") | ||||
|         self.verticalLayout_4.addWidget(self.tag_implication_search_line) | ||||
|         self.tag_implication_label_frame = QtWidgets.QFrame(self.tag_implication_frame) | ||||
|         self.tag_implication_label_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.tag_implication_label_frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.tag_implication_label_frame.setObjectName("tag_implication_label_frame") | ||||
|         self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.tag_implication_label_frame) | ||||
|         self.horizontalLayout_3.setObjectName("horizontalLayout_3") | ||||
|         self.tag_implication_search_label = QtWidgets.QLabel(self.tag_implication_label_frame) | ||||
|         self.tag_implication_search_label.setObjectName("tag_implication_search_label") | ||||
|         self.horizontalLayout_3.addWidget(self.tag_implication_search_label) | ||||
|         self.tag_implication_selection_label = QtWidgets.QLabel(self.tag_implication_label_frame) | ||||
|         self.tag_implication_selection_label.setObjectName("tag_implication_selection_label") | ||||
|         self.horizontalLayout_3.addWidget(self.tag_implication_selection_label) | ||||
|         self.verticalLayout_4.addWidget(self.tag_implication_label_frame) | ||||
|         self.tag_implication_list_frame = QtWidgets.QFrame(self.tag_implication_frame) | ||||
|         self.tag_implication_list_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.tag_implication_list_frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.tag_implication_list_frame.setObjectName("tag_implication_list_frame") | ||||
|         self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.tag_implication_list_frame) | ||||
|         self.horizontalLayout_4.setObjectName("horizontalLayout_4") | ||||
|         self.tag_implication_search_list = QtWidgets.QListView(self.tag_implication_list_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.tag_implication_search_list.sizePolicy().hasHeightForWidth()) | ||||
|         self.tag_implication_search_list.setSizePolicy(sizePolicy) | ||||
|         self.tag_implication_search_list.setMaximumSize(QtCore.QSize(16777215, 100)) | ||||
|         self.tag_implication_search_list.setObjectName("tag_implication_search_list") | ||||
|         self.horizontalLayout_4.addWidget(self.tag_implication_search_list) | ||||
|         self.tag_implication_selection_list = QtWidgets.QListView(self.tag_implication_list_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.tag_implication_selection_list.sizePolicy().hasHeightForWidth()) | ||||
|         self.tag_implication_selection_list.setSizePolicy(sizePolicy) | ||||
|         self.tag_implication_selection_list.setMinimumSize(QtCore.QSize(0, 0)) | ||||
|         self.tag_implication_selection_list.setMaximumSize(QtCore.QSize(16777215, 100)) | ||||
|         self.tag_implication_selection_list.setObjectName("tag_implication_selection_list") | ||||
|         self.horizontalLayout_4.addWidget(self.tag_implication_selection_list) | ||||
|         self.verticalLayout_4.addWidget(self.tag_implication_list_frame) | ||||
|         self.verticalLayout_2.addWidget(self.tag_implication_frame) | ||||
|         self.verticalLayout.addWidget(self.dialog_frame) | ||||
|         self.buttonBox = QtWidgets.QDialogButtonBox(TagModify) | ||||
|         self.buttonBox.setOrientation(QtCore.Qt.Horizontal) | ||||
|         self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) | ||||
|         self.buttonBox.setObjectName("buttonBox") | ||||
|         self.verticalLayout.addWidget(self.buttonBox) | ||||
| 
 | ||||
|         self.retranslateUi(TagModify) | ||||
|         self.buttonBox.accepted.connect(TagModify.accept) | ||||
|         self.buttonBox.rejected.connect(TagModify.reject) | ||||
|         QtCore.QMetaObject.connectSlotsByName(TagModify) | ||||
| 
 | ||||
|     def retranslateUi(self, TagModify): | ||||
|         _translate = QtCore.QCoreApplication.translate | ||||
|         TagModify.setWindowTitle(_translate("TagModify", "Dialog")) | ||||
|         self.dialog_topic.setText(_translate("TagModify", "Tag Topic")) | ||||
|         self.tag_name_label.setText(_translate("TagModify", "Tag Name:")) | ||||
|         self.tag_description_label.setText(_translate("TagModify", "Description:")) | ||||
|         self.category_label.setText(_translate("TagModify", "Category:")) | ||||
|         self.tag_alias_label.setText(_translate("TagModify", "Tag Aliases")) | ||||
|         self.tag_alias_search_label.setText(_translate("TagModify", "Search Result:")) | ||||
|         self.tag_alias_selection_label.setText(_translate("TagModify", "Selection:")) | ||||
|         self.tag_implication_label.setText(_translate("TagModify", "Tag Implications")) | ||||
|         self.tag_implication_search_label.setText(_translate("TagModify", "Search Result:")) | ||||
|         self.tag_implication_selection_label.setText(_translate("TagModify", "Selection:")) | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     import sys | ||||
|     app = QtWidgets.QApplication(sys.argv) | ||||
|     TagModify = QtWidgets.QDialog() | ||||
|     ui = Ui_TagModify() | ||||
|     ui.setupUi(TagModify) | ||||
|     TagModify.show() | ||||
|     sys.exit(app.exec_()) | ||||
| @ -0,0 +1,375 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>TagModify</class> | ||||
|  <widget class="QDialog" name="TagModify"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>587</width> | ||||
|     <height>846</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Dialog</string> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|    <item> | ||||
|     <widget class="QFrame" name="dialog_frame"> | ||||
|      <property name="sizePolicy"> | ||||
|       <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> | ||||
|        <horstretch>0</horstretch> | ||||
|        <verstretch>0</verstretch> | ||||
|       </sizepolicy> | ||||
|      </property> | ||||
|      <property name="frameShape"> | ||||
|       <enum>QFrame::StyledPanel</enum> | ||||
|      </property> | ||||
|      <property name="frameShadow"> | ||||
|       <enum>QFrame::Raised</enum> | ||||
|      </property> | ||||
|      <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="dialog_topic"> | ||||
|         <property name="sizePolicy"> | ||||
|          <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|           <horstretch>0</horstretch> | ||||
|           <verstretch>0</verstretch> | ||||
|          </sizepolicy> | ||||
|         </property> | ||||
|         <property name="font"> | ||||
|          <font> | ||||
|           <pointsize>10</pointsize> | ||||
|           <weight>75</weight> | ||||
|           <bold>true</bold> | ||||
|          </font> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Tag Topic</string> | ||||
|         </property> | ||||
|         <property name="alignment"> | ||||
|          <set>Qt::AlignCenter</set> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="tag_name_label"> | ||||
|         <property name="sizePolicy"> | ||||
|          <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|           <horstretch>0</horstretch> | ||||
|           <verstretch>0</verstretch> | ||||
|          </sizepolicy> | ||||
|         </property> | ||||
|         <property name="font"> | ||||
|          <font> | ||||
|           <weight>75</weight> | ||||
|           <bold>true</bold> | ||||
|          </font> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Tag Name:</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLineEdit" name="tag_name_line"/> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="tag_description_label"> | ||||
|         <property name="sizePolicy"> | ||||
|          <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|           <horstretch>0</horstretch> | ||||
|           <verstretch>0</verstretch> | ||||
|          </sizepolicy> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Description:</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QTextEdit" name="tag_description_area"> | ||||
|         <property name="maximumSize"> | ||||
|          <size> | ||||
|           <width>16777215</width> | ||||
|           <height>100</height> | ||||
|          </size> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="category_label"> | ||||
|         <property name="text"> | ||||
|          <string>Category:</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QListView" name="category_list"> | ||||
|         <property name="maximumSize"> | ||||
|          <size> | ||||
|           <width>16777215</width> | ||||
|           <height>100</height> | ||||
|          </size> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QFrame" name="tag_alias_frame"> | ||||
|         <property name="frameShape"> | ||||
|          <enum>QFrame::StyledPanel</enum> | ||||
|         </property> | ||||
|         <property name="frameShadow"> | ||||
|          <enum>QFrame::Raised</enum> | ||||
|         </property> | ||||
|         <layout class="QVBoxLayout" name="verticalLayout_3"> | ||||
|          <item> | ||||
|           <widget class="QLabel" name="tag_alias_label"> | ||||
|            <property name="font"> | ||||
|             <font> | ||||
|              <weight>75</weight> | ||||
|              <bold>true</bold> | ||||
|             </font> | ||||
|            </property> | ||||
|            <property name="text"> | ||||
|             <string>Tag Aliases</string> | ||||
|            </property> | ||||
|            <property name="alignment"> | ||||
|             <set>Qt::AlignCenter</set> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QLineEdit" name="tag_alias_search_line"/> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QFrame" name="tag_alias_label_frame"> | ||||
|            <property name="frameShape"> | ||||
|             <enum>QFrame::StyledPanel</enum> | ||||
|            </property> | ||||
|            <property name="frameShadow"> | ||||
|             <enum>QFrame::Raised</enum> | ||||
|            </property> | ||||
|            <layout class="QHBoxLayout" name="horizontalLayout_2"> | ||||
|             <item> | ||||
|              <widget class="QLabel" name="tag_alias_search_label"> | ||||
|               <property name="text"> | ||||
|                <string>Search Result:</string> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|             <item> | ||||
|              <widget class="QLabel" name="tag_alias_selection_label"> | ||||
|               <property name="text"> | ||||
|                <string>Selection:</string> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|            </layout> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QFrame" name="tag_alias_list_frame"> | ||||
|            <property name="frameShape"> | ||||
|             <enum>QFrame::StyledPanel</enum> | ||||
|            </property> | ||||
|            <property name="frameShadow"> | ||||
|             <enum>QFrame::Raised</enum> | ||||
|            </property> | ||||
|            <layout class="QHBoxLayout" name="horizontalLayout"> | ||||
|             <item> | ||||
|              <widget class="QListView" name="tag_alias_search_list"> | ||||
|               <property name="sizePolicy"> | ||||
|                <sizepolicy hsizetype="Expanding" vsizetype="Minimum"> | ||||
|                 <horstretch>0</horstretch> | ||||
|                 <verstretch>0</verstretch> | ||||
|                </sizepolicy> | ||||
|               </property> | ||||
|               <property name="maximumSize"> | ||||
|                <size> | ||||
|                 <width>16777215</width> | ||||
|                 <height>100</height> | ||||
|                </size> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|             <item> | ||||
|              <widget class="QListView" name="tag_alias_selection_list"> | ||||
|               <property name="sizePolicy"> | ||||
|                <sizepolicy hsizetype="Expanding" vsizetype="Minimum"> | ||||
|                 <horstretch>0</horstretch> | ||||
|                 <verstretch>0</verstretch> | ||||
|                </sizepolicy> | ||||
|               </property> | ||||
|               <property name="maximumSize"> | ||||
|                <size> | ||||
|                 <width>16777215</width> | ||||
|                 <height>100</height> | ||||
|                </size> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|            </layout> | ||||
|           </widget> | ||||
|          </item> | ||||
|         </layout> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QFrame" name="tag_implication_frame"> | ||||
|         <property name="frameShape"> | ||||
|          <enum>QFrame::StyledPanel</enum> | ||||
|         </property> | ||||
|         <property name="frameShadow"> | ||||
|          <enum>QFrame::Raised</enum> | ||||
|         </property> | ||||
|         <layout class="QVBoxLayout" name="verticalLayout_4"> | ||||
|          <item> | ||||
|           <widget class="QLabel" name="tag_implication_label"> | ||||
|            <property name="font"> | ||||
|             <font> | ||||
|              <weight>75</weight> | ||||
|              <bold>true</bold> | ||||
|             </font> | ||||
|            </property> | ||||
|            <property name="text"> | ||||
|             <string>Tag Implications</string> | ||||
|            </property> | ||||
|            <property name="alignment"> | ||||
|             <set>Qt::AlignCenter</set> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QLineEdit" name="tag_implication_search_line"/> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QFrame" name="tag_implication_label_frame"> | ||||
|            <property name="frameShape"> | ||||
|             <enum>QFrame::StyledPanel</enum> | ||||
|            </property> | ||||
|            <property name="frameShadow"> | ||||
|             <enum>QFrame::Raised</enum> | ||||
|            </property> | ||||
|            <layout class="QHBoxLayout" name="horizontalLayout_3"> | ||||
|             <item> | ||||
|              <widget class="QLabel" name="tag_implication_search_label"> | ||||
|               <property name="text"> | ||||
|                <string>Search Result:</string> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|             <item> | ||||
|              <widget class="QLabel" name="tag_implication_selection_label"> | ||||
|               <property name="text"> | ||||
|                <string>Selection:</string> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|            </layout> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QFrame" name="tag_implication_list_frame"> | ||||
|            <property name="frameShape"> | ||||
|             <enum>QFrame::StyledPanel</enum> | ||||
|            </property> | ||||
|            <property name="frameShadow"> | ||||
|             <enum>QFrame::Raised</enum> | ||||
|            </property> | ||||
|            <layout class="QHBoxLayout" name="horizontalLayout_4"> | ||||
|             <item> | ||||
|              <widget class="QListView" name="tag_implication_search_list"> | ||||
|               <property name="sizePolicy"> | ||||
|                <sizepolicy hsizetype="Expanding" vsizetype="Minimum"> | ||||
|                 <horstretch>0</horstretch> | ||||
|                 <verstretch>0</verstretch> | ||||
|                </sizepolicy> | ||||
|               </property> | ||||
|               <property name="maximumSize"> | ||||
|                <size> | ||||
|                 <width>16777215</width> | ||||
|                 <height>100</height> | ||||
|                </size> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|             <item> | ||||
|              <widget class="QListView" name="tag_implication_selection_list"> | ||||
|               <property name="sizePolicy"> | ||||
|                <sizepolicy hsizetype="Expanding" vsizetype="Minimum"> | ||||
|                 <horstretch>0</horstretch> | ||||
|                 <verstretch>0</verstretch> | ||||
|                </sizepolicy> | ||||
|               </property> | ||||
|               <property name="minimumSize"> | ||||
|                <size> | ||||
|                 <width>0</width> | ||||
|                 <height>0</height> | ||||
|                </size> | ||||
|               </property> | ||||
|               <property name="maximumSize"> | ||||
|                <size> | ||||
|                 <width>16777215</width> | ||||
|                 <height>100</height> | ||||
|                </size> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|            </layout> | ||||
|           </widget> | ||||
|          </item> | ||||
|         </layout> | ||||
|        </widget> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QDialogButtonBox" name="buttonBox"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Horizontal</enum> | ||||
|      </property> | ||||
|      <property name="standardButtons"> | ||||
|       <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>accepted()</signal> | ||||
|    <receiver>TagModify</receiver> | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>248</x> | ||||
|      <y>254</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>157</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>rejected()</signal> | ||||
|    <receiver>TagModify</receiver> | ||||
|    <slot>reject()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>316</x> | ||||
|      <y>260</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>286</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|  </connections> | ||||
| </ui> | ||||
| @ -0,0 +1,77 @@ | ||||
| from PyQt5 import QtWidgets | ||||
| from PyQt5.QtGui import QStandardItemModel, QStandardItem | ||||
| from PyQt5.QtCore import Qt | ||||
| 
 | ||||
| from ArtNet.gui.dialogs.tag_select_dialog.tag_selection_dialog import Ui_TagSelection | ||||
| 
 | ||||
| 
 | ||||
| class TagSelectDialog(QtWidgets.QDialog): | ||||
| 
 | ||||
|     def __init__(self, parent=None, delete_tag: bool=False): | ||||
|         super().__init__(parent) | ||||
|         self.parent = parent | ||||
|         self.ui = Ui_TagSelection() | ||||
|         self.ui.setupUi(self) | ||||
| 
 | ||||
|         if delete_tag: | ||||
|             self.ui.select_tag_label.setText("Select Tag to delete") | ||||
| 
 | ||||
|         self.selected_item: QStandardItem = None | ||||
|         self.selection: str = None | ||||
| 
 | ||||
|         self.ui.buttonBox.accepted.connect(self.getTagSelection) | ||||
|         self.ui.tag_search_line.textChanged.connect(self.getSearchResult) | ||||
|         self.getSearchResult() | ||||
| 
 | ||||
|     def getSearchResult(self): | ||||
|         search_text = self.ui.tag_search_line.text() | ||||
|         tags = self.parent.get_tag_search_result(search_text) | ||||
| 
 | ||||
|         if tags is None: | ||||
|             return | ||||
|         self.set_search_result_list(tags) | ||||
| 
 | ||||
|     def getTagSelection(self) -> str: | ||||
|         if self.selected_item is None: | ||||
|             return | ||||
|         self.selection = self.selected_item.text() | ||||
| 
 | ||||
|     def on_tag_item_changed(self, item: QStandardItem): | ||||
|         if self.selected_item is not None: | ||||
|             self.selected_item.setCheckState(Qt.Unchecked) | ||||
|         self.selected_item = item | ||||
| 
 | ||||
|     def set_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[0]) | ||||
|             item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) | ||||
|             item.setData(Qt.Unchecked, Qt.CheckStateRole) | ||||
| 
 | ||||
|             item_model.appendRow(item) | ||||
| 
 | ||||
|         item_model.itemChanged.connect(self.on_tag_item_changed) | ||||
|         self.ui.search_result_list.setModel(item_model) | ||||
| 
 | ||||
|     def exec_(self) -> dict: | ||||
|         if super(TagSelectDialog, self).exec_() == QtWidgets.QDialog.Rejected: | ||||
|             return None | ||||
| 
 | ||||
|         if self.selection is None: | ||||
|             return None | ||||
|         tag = self.parent.get_tag(self.selection)[0] | ||||
|         tag_data = { | ||||
|             "ID": tag[3], | ||||
|             "name": tag[0], | ||||
|             "description": tag[1], | ||||
|             "aliases": self.parent.get_tag_aliases(tag[0]), | ||||
|             "implications": self.parent.get_tag_implications(tag[0]), | ||||
|             "category": tag[2] | ||||
|         } | ||||
|         return tag_data | ||||
| @ -0,0 +1,70 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| 
 | ||||
| # Form implementation generated from reading ui file 'dialogs/tag_select_dialog/tag_selection_dialog.ui' | ||||
| # | ||||
| # Created by: PyQt5 UI code generator 5.15.0 | ||||
| # | ||||
| # WARNING: Any manual changes made to this file will be lost when pyuic5 is | ||||
| # run again.  Do not edit this file unless you know what you are doing. | ||||
| 
 | ||||
| 
 | ||||
| from PyQt5 import QtCore, QtGui, QtWidgets | ||||
| 
 | ||||
| 
 | ||||
| class Ui_TagSelection(object): | ||||
|     def setupUi(self, TagSelection): | ||||
|         TagSelection.setObjectName("TagSelection") | ||||
|         TagSelection.resize(400, 300) | ||||
|         self.verticalLayout_2 = QtWidgets.QVBoxLayout(TagSelection) | ||||
|         self.verticalLayout_2.setObjectName("verticalLayout_2") | ||||
|         self.top_frame = QtWidgets.QFrame(TagSelection) | ||||
|         self.top_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.top_frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.top_frame.setObjectName("top_frame") | ||||
|         self.verticalLayout = QtWidgets.QVBoxLayout(self.top_frame) | ||||
|         self.verticalLayout.setObjectName("verticalLayout") | ||||
|         self.select_tag_label = QtWidgets.QLabel(self.top_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.select_tag_label.sizePolicy().hasHeightForWidth()) | ||||
|         self.select_tag_label.setSizePolicy(sizePolicy) | ||||
|         font = QtGui.QFont() | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.select_tag_label.setFont(font) | ||||
|         self.select_tag_label.setAlignment(QtCore.Qt.AlignCenter) | ||||
|         self.select_tag_label.setObjectName("select_tag_label") | ||||
|         self.verticalLayout.addWidget(self.select_tag_label) | ||||
|         self.tag_search_line = QtWidgets.QLineEdit(self.top_frame) | ||||
|         self.tag_search_line.setObjectName("tag_search_line") | ||||
|         self.verticalLayout.addWidget(self.tag_search_line) | ||||
|         self.search_result_list = QtWidgets.QListView(self.top_frame) | ||||
|         self.search_result_list.setObjectName("search_result_list") | ||||
|         self.verticalLayout.addWidget(self.search_result_list) | ||||
|         self.verticalLayout_2.addWidget(self.top_frame) | ||||
|         self.buttonBox = QtWidgets.QDialogButtonBox(TagSelection) | ||||
|         self.buttonBox.setOrientation(QtCore.Qt.Horizontal) | ||||
|         self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) | ||||
|         self.buttonBox.setObjectName("buttonBox") | ||||
|         self.verticalLayout_2.addWidget(self.buttonBox) | ||||
| 
 | ||||
|         self.retranslateUi(TagSelection) | ||||
|         self.buttonBox.accepted.connect(TagSelection.accept) | ||||
|         self.buttonBox.rejected.connect(TagSelection.reject) | ||||
|         QtCore.QMetaObject.connectSlotsByName(TagSelection) | ||||
| 
 | ||||
|     def retranslateUi(self, TagSelection): | ||||
|         _translate = QtCore.QCoreApplication.translate | ||||
|         TagSelection.setWindowTitle(_translate("TagSelection", "Dialog")) | ||||
|         self.select_tag_label.setText(_translate("TagSelection", "Select Tag")) | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     import sys | ||||
|     app = QtWidgets.QApplication(sys.argv) | ||||
|     TagSelection = QtWidgets.QDialog() | ||||
|     ui = Ui_TagSelection() | ||||
|     ui.setupUi(TagSelection) | ||||
|     TagSelection.show() | ||||
|     sys.exit(app.exec_()) | ||||
| @ -0,0 +1,104 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>TagSelection</class> | ||||
|  <widget class="QDialog" name="TagSelection"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>400</width> | ||||
|     <height>300</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Dialog</string> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||
|    <item> | ||||
|     <widget class="QFrame" name="top_frame"> | ||||
|      <property name="frameShape"> | ||||
|       <enum>QFrame::StyledPanel</enum> | ||||
|      </property> | ||||
|      <property name="frameShadow"> | ||||
|       <enum>QFrame::Raised</enum> | ||||
|      </property> | ||||
|      <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="select_tag_label"> | ||||
|         <property name="sizePolicy"> | ||||
|          <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|           <horstretch>0</horstretch> | ||||
|           <verstretch>0</verstretch> | ||||
|          </sizepolicy> | ||||
|         </property> | ||||
|         <property name="font"> | ||||
|          <font> | ||||
|           <weight>75</weight> | ||||
|           <bold>true</bold> | ||||
|          </font> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Select Tag</string> | ||||
|         </property> | ||||
|         <property name="alignment"> | ||||
|          <set>Qt::AlignCenter</set> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLineEdit" name="tag_search_line"/> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QListView" name="search_result_list"/> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QDialogButtonBox" name="buttonBox"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Horizontal</enum> | ||||
|      </property> | ||||
|      <property name="standardButtons"> | ||||
|       <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>accepted()</signal> | ||||
|    <receiver>TagSelection</receiver> | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>248</x> | ||||
|      <y>254</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>157</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>rejected()</signal> | ||||
|    <receiver>TagSelection</receiver> | ||||
|    <slot>reject()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>316</x> | ||||
|      <y>260</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>286</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|  </connections> | ||||
| </ui> | ||||
| @ -0,0 +1,146 @@ | ||||
| from PyQt5 import QtWidgets | ||||
| from PyQt5.QtGui import QStandardItem, QStandardItemModel | ||||
| from PyQt5.QtCore import Qt | ||||
| 
 | ||||
| from ArtNet.gui.dockers.presence.presence_docker import Ui_presence_docker | ||||
| from ArtNet.gui.dialogs.presence_modify_dialog.presence_mod_dialog import PresenceModDialog | ||||
| from ArtNet.gui.dialogs.presence_selection_dialog.presence_select_dialog import PresenceSelectDialog | ||||
| 
 | ||||
| 
 | ||||
| class PresenceDocker(QtWidgets.QFrame): | ||||
| 
 | ||||
|     def __init__(self, parent=None): | ||||
|         super().__init__(parent) | ||||
|         self.parent = parent | ||||
| 
 | ||||
|         self.ui = Ui_presence_docker() | ||||
|         self.ui.setupUi(self) | ||||
| 
 | ||||
|         self.ui.name_line.textChanged.connect(self.on_search_bar_change) | ||||
|         self.ui.domain_line.textChanged.connect(self.on_search_bar_change) | ||||
| 
 | ||||
|         self.ui.create_presence_button.clicked.connect(self.on_create_presence_clicked) | ||||
|         self.ui.edit_presence_button.clicked.connect(self.on_edit_presence_clicked) | ||||
|         self.ui.delete_presence_button.clicked.connect(self.on_delete_presence_clicked) | ||||
| 
 | ||||
|         self.on_search_bar_change() | ||||
| 
 | ||||
|     def set_presence_list(self, presences: list): | ||||
|         item_model = QStandardItemModel(self.ui.search_author_list) | ||||
| 
 | ||||
|         for name, domain in presences: | ||||
|             item = QStandardItem(name+":"+domain) | ||||
|             item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) | ||||
|             item.setData(Qt.Unchecked, Qt.CheckStateRole) | ||||
| 
 | ||||
|             item_model.appendRow(item) | ||||
|             if (name, domain) in self.parent.get_current_presences(): | ||||
|                 item.setCheckState(Qt.Checked) | ||||
| 
 | ||||
|         item_model.itemChanged.connect(self.on_presence_item_change) | ||||
|         self.ui.search_author_list.setModel(item_model) | ||||
| 
 | ||||
|     def set_selected_presences_list(self, presences: list): | ||||
|         item_model = QStandardItemModel(self.ui.selected_presence_list) | ||||
| 
 | ||||
|         for name, domain in presences: | ||||
|             item = QStandardItem(name + ":" + domain) | ||||
|             item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) | ||||
|             item.setData(Qt.Unchecked, Qt.CheckStateRole) | ||||
| 
 | ||||
|             if domain == "(Not in Database)": | ||||
|                 continue | ||||
| 
 | ||||
|             item_model.appendRow(item) | ||||
|             if (name, domain) in self.parent.get_current_presences(): | ||||
|                 item.setCheckState(Qt.Checked) | ||||
| 
 | ||||
|         item_model.itemChanged.connect(self.on_selected_presence_item_change) | ||||
|         self.ui.selected_presence_list.setModel(item_model) | ||||
| 
 | ||||
|     def on_search_bar_change(self): | ||||
|         authors = self.parent.get_authors(self.ui.name_line.text(), self.ui.domain_line.text()) | ||||
|         self.set_presence_list(authors) | ||||
| 
 | ||||
|     def on_presence_item_change(self, item: QStandardItem): | ||||
|         if item.checkState() == Qt.Checked: | ||||
|             s = item.text().split(":") | ||||
|             s = (s[0], s[1]) | ||||
|             if self.parent.get_current_presences() is None: | ||||
|                 self.parent.set_current_presences([s]) | ||||
|                 self.set_selected_presences_list([s]) | ||||
|             else: | ||||
|                 self.parent.set_current_presences(self.parent.get_current_presences() + [s]) | ||||
|                 self.set_selected_presences_list(self.parent.get_current_presences()) | ||||
|         elif item.checkState() == Qt.Unchecked: | ||||
|             presences = self.parent.get_current_presences() | ||||
|             if presences is None: | ||||
|                 return | ||||
|             s = item.text().split(":") | ||||
|             s = (s[0], s[1]) | ||||
|             presences.remove(s) | ||||
|             self.parent.set_current_presences(presences) | ||||
|             self.set_selected_presences_list(presences) | ||||
| 
 | ||||
|     def on_selected_presence_item_change(self, item: QStandardItem): | ||||
|         item_presence = item.text().split(":") | ||||
|         item_presence = (item_presence[0], item_presence[1]) | ||||
|         if item.checkState() == Qt.Unchecked: | ||||
|             presences = self.parent.get_current_presences() | ||||
|             presences.remove(item_presence) | ||||
|             self.parent.set_current_presences(presences) | ||||
| 
 | ||||
|             self.set_selected_presences_list(presences) | ||||
|             self.on_search_bar_change() | ||||
| 
 | ||||
|     def on_create_presence_clicked(self): | ||||
|         dialog = PresenceModDialog(self, edit_presence=False) | ||||
|         data = dialog.exec_() | ||||
|         if data is None:  # canceled? | ||||
|             self.on_search_bar_change() | ||||
|             return | ||||
|         self.parent.create_presence(name=data["name"], domain=data["domain"], artist=data["artist"], | ||||
|                                     link=data["link"]) | ||||
|         self.on_search_bar_change() | ||||
| 
 | ||||
|     def on_edit_presence_clicked(self): | ||||
|         select_dialog = PresenceSelectDialog(self) | ||||
|         selected_presence = select_dialog.exec_() | ||||
|         if selected_presence is None: | ||||
|             return | ||||
| 
 | ||||
|         dialog = PresenceModDialog(self, edit_presence=True) | ||||
|         dialog.ui.presence_name_line.setText(selected_presence[0]) | ||||
|         dialog.ui.presence_domain_line.setText(selected_presence[1]) | ||||
| 
 | ||||
|         if len(selected_presence) > 2: | ||||
|             if len(selected_presence) >= 4: | ||||
|                 if selected_presence[3] is not None: | ||||
|                     dialog.ui.presence_link_list.setText(selected_presence[3]) | ||||
| 
 | ||||
|             artist = self.parent.get_artists(selected_presence[2])[0] | ||||
|             dialog.curr_artist = artist | ||||
|             dialog.set_artist_selected_list(dialog.curr_artist) | ||||
| 
 | ||||
|         data = dialog.exec_() | ||||
|         if data is None: | ||||
|             self.on_search_bar_change() | ||||
|             return | ||||
| 
 | ||||
|         self.parent.create_presence(name=data["name"], domain=data["domain"], artist=data["artist"], link=data["link"]) | ||||
|         self.parent.set_temporary_status_message("Saved Presence {0}:{1} Artist:{2} link:{3} to DB!" | ||||
|                                                  .format(data["name"], data["domain"], data["artist"], data["link"]), | ||||
|                                                  3000) | ||||
|         self.on_search_bar_change() | ||||
| 
 | ||||
|     def on_delete_presence_clicked(self): | ||||
|         print("Clicked delete presence button!") | ||||
|         select_dialog = PresenceSelectDialog(self) | ||||
|         selected_presence = select_dialog.exec_() | ||||
|         if selected_presence is None: | ||||
|             self.on_search_bar_change() | ||||
|             return | ||||
| 
 | ||||
|         self.parent.remove_presence(selected_presence[0], selected_presence[1]) | ||||
| 
 | ||||
|         self.on_search_bar_change() | ||||
| @ -0,0 +1,143 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| 
 | ||||
| # Form implementation generated from reading ui file 'dockers/presence/presence_docker.ui' | ||||
| # | ||||
| # Created by: PyQt5 UI code generator 5.15.0 | ||||
| # | ||||
| # WARNING: Any manual changes made to this file will be lost when pyuic5 is | ||||
| # run again.  Do not edit this file unless you know what you are doing. | ||||
| 
 | ||||
| 
 | ||||
| from PyQt5 import QtCore, QtGui, QtWidgets | ||||
| 
 | ||||
| 
 | ||||
| class Ui_presence_docker(object): | ||||
|     def setupUi(self, presence_docker): | ||||
|         presence_docker.setObjectName("presence_docker") | ||||
|         presence_docker.resize(338, 576) | ||||
|         self.verticalLayout = QtWidgets.QVBoxLayout(presence_docker) | ||||
|         self.verticalLayout.setObjectName("verticalLayout") | ||||
|         self.frame = QtWidgets.QFrame(presence_docker) | ||||
|         self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.frame.setObjectName("frame") | ||||
|         self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.frame) | ||||
|         self.verticalLayout_2.setObjectName("verticalLayout_2") | ||||
|         self.docker_topic_label = QtWidgets.QLabel(self.frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.docker_topic_label.sizePolicy().hasHeightForWidth()) | ||||
|         self.docker_topic_label.setSizePolicy(sizePolicy) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(13) | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.docker_topic_label.setFont(font) | ||||
|         self.docker_topic_label.setAlignment(QtCore.Qt.AlignCenter) | ||||
|         self.docker_topic_label.setObjectName("docker_topic_label") | ||||
|         self.verticalLayout_2.addWidget(self.docker_topic_label) | ||||
|         self.search_author_label = QtWidgets.QLabel(self.frame) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(10) | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.search_author_label.setFont(font) | ||||
|         self.search_author_label.setObjectName("search_author_label") | ||||
|         self.verticalLayout_2.addWidget(self.search_author_label) | ||||
|         self.search_frame = QtWidgets.QFrame(self.frame) | ||||
|         self.search_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.search_frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.search_frame.setObjectName("search_frame") | ||||
|         self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.search_frame) | ||||
|         self.horizontalLayout_3.setObjectName("horizontalLayout_3") | ||||
|         self.name_frame = QtWidgets.QFrame(self.search_frame) | ||||
|         self.name_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.name_frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.name_frame.setObjectName("name_frame") | ||||
|         self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.name_frame) | ||||
|         self.verticalLayout_3.setObjectName("verticalLayout_3") | ||||
|         self.name_label = QtWidgets.QLabel(self.name_frame) | ||||
|         font = QtGui.QFont() | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.name_label.setFont(font) | ||||
|         self.name_label.setObjectName("name_label") | ||||
|         self.verticalLayout_3.addWidget(self.name_label) | ||||
|         self.name_line = QtWidgets.QLineEdit(self.name_frame) | ||||
|         self.name_line.setObjectName("name_line") | ||||
|         self.verticalLayout_3.addWidget(self.name_line) | ||||
|         self.horizontalLayout_3.addWidget(self.name_frame) | ||||
|         self.domain_frame = QtWidgets.QFrame(self.search_frame) | ||||
|         self.domain_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.domain_frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.domain_frame.setObjectName("domain_frame") | ||||
|         self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.domain_frame) | ||||
|         self.verticalLayout_4.setObjectName("verticalLayout_4") | ||||
|         self.domain_label = QtWidgets.QLabel(self.domain_frame) | ||||
|         font = QtGui.QFont() | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.domain_label.setFont(font) | ||||
|         self.domain_label.setObjectName("domain_label") | ||||
|         self.verticalLayout_4.addWidget(self.domain_label) | ||||
|         self.domain_line = QtWidgets.QLineEdit(self.domain_frame) | ||||
|         self.domain_line.setObjectName("domain_line") | ||||
|         self.verticalLayout_4.addWidget(self.domain_line) | ||||
|         self.horizontalLayout_3.addWidget(self.domain_frame) | ||||
|         self.verticalLayout_2.addWidget(self.search_frame) | ||||
|         self.search_author_list = QtWidgets.QListView(self.frame) | ||||
|         self.search_author_list.setObjectName("search_author_list") | ||||
|         self.verticalLayout_2.addWidget(self.search_author_list) | ||||
|         self.selected_presence_label = QtWidgets.QLabel(self.frame) | ||||
|         font = QtGui.QFont() | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.selected_presence_label.setFont(font) | ||||
|         self.selected_presence_label.setObjectName("selected_presence_label") | ||||
|         self.verticalLayout_2.addWidget(self.selected_presence_label) | ||||
|         self.selected_presence_list = QtWidgets.QListView(self.frame) | ||||
|         self.selected_presence_list.setObjectName("selected_presence_list") | ||||
|         self.verticalLayout_2.addWidget(self.selected_presence_list) | ||||
|         self.presence_docker_button_frame = QtWidgets.QFrame(self.frame) | ||||
|         self.presence_docker_button_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.presence_docker_button_frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.presence_docker_button_frame.setObjectName("presence_docker_button_frame") | ||||
|         self.horizontalLayout = QtWidgets.QHBoxLayout(self.presence_docker_button_frame) | ||||
|         self.horizontalLayout.setObjectName("horizontalLayout") | ||||
|         self.create_presence_button = QtWidgets.QPushButton(self.presence_docker_button_frame) | ||||
|         self.create_presence_button.setObjectName("create_presence_button") | ||||
|         self.horizontalLayout.addWidget(self.create_presence_button) | ||||
|         self.edit_presence_button = QtWidgets.QPushButton(self.presence_docker_button_frame) | ||||
|         self.edit_presence_button.setObjectName("edit_presence_button") | ||||
|         self.horizontalLayout.addWidget(self.edit_presence_button) | ||||
|         self.delete_presence_button = QtWidgets.QPushButton(self.presence_docker_button_frame) | ||||
|         self.delete_presence_button.setObjectName("delete_presence_button") | ||||
|         self.horizontalLayout.addWidget(self.delete_presence_button) | ||||
|         self.verticalLayout_2.addWidget(self.presence_docker_button_frame) | ||||
|         self.verticalLayout.addWidget(self.frame) | ||||
| 
 | ||||
|         self.retranslateUi(presence_docker) | ||||
|         QtCore.QMetaObject.connectSlotsByName(presence_docker) | ||||
| 
 | ||||
|     def retranslateUi(self, presence_docker): | ||||
|         _translate = QtCore.QCoreApplication.translate | ||||
|         presence_docker.setWindowTitle(_translate("presence_docker", "Form")) | ||||
|         self.docker_topic_label.setText(_translate("presence_docker", "Author (Presence)")) | ||||
|         self.search_author_label.setText(_translate("presence_docker", "Search Authors:")) | ||||
|         self.name_label.setText(_translate("presence_docker", "Name:")) | ||||
|         self.domain_label.setText(_translate("presence_docker", "Domain:")) | ||||
|         self.selected_presence_label.setText(_translate("presence_docker", "Selected Presences:")) | ||||
|         self.create_presence_button.setText(_translate("presence_docker", "Create Presence")) | ||||
|         self.edit_presence_button.setText(_translate("presence_docker", "Edit Presence")) | ||||
|         self.delete_presence_button.setText(_translate("presence_docker", "Delete Presence")) | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     import sys | ||||
|     app = QtWidgets.QApplication(sys.argv) | ||||
|     presence_docker = QtWidgets.QWidget() | ||||
|     ui = Ui_presence_docker() | ||||
|     ui.setupUi(presence_docker) | ||||
|     presence_docker.show() | ||||
|     sys.exit(app.exec_()) | ||||
| @ -0,0 +1,190 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>presence_docker</class> | ||||
|  <widget class="QWidget" name="presence_docker"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>338</width> | ||||
|     <height>576</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Form</string> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|    <item> | ||||
|     <widget class="QFrame" name="frame"> | ||||
|      <property name="frameShape"> | ||||
|       <enum>QFrame::StyledPanel</enum> | ||||
|      </property> | ||||
|      <property name="frameShadow"> | ||||
|       <enum>QFrame::Raised</enum> | ||||
|      </property> | ||||
|      <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="docker_topic_label"> | ||||
|         <property name="sizePolicy"> | ||||
|          <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|           <horstretch>0</horstretch> | ||||
|           <verstretch>0</verstretch> | ||||
|          </sizepolicy> | ||||
|         </property> | ||||
|         <property name="font"> | ||||
|          <font> | ||||
|           <pointsize>13</pointsize> | ||||
|           <weight>75</weight> | ||||
|           <bold>true</bold> | ||||
|          </font> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Author (Presence)</string> | ||||
|         </property> | ||||
|         <property name="alignment"> | ||||
|          <set>Qt::AlignCenter</set> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="search_author_label"> | ||||
|         <property name="font"> | ||||
|          <font> | ||||
|           <pointsize>10</pointsize> | ||||
|           <weight>75</weight> | ||||
|           <bold>true</bold> | ||||
|          </font> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Search Authors:</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QFrame" name="search_frame"> | ||||
|         <property name="frameShape"> | ||||
|          <enum>QFrame::StyledPanel</enum> | ||||
|         </property> | ||||
|         <property name="frameShadow"> | ||||
|          <enum>QFrame::Raised</enum> | ||||
|         </property> | ||||
|         <layout class="QHBoxLayout" name="horizontalLayout_3"> | ||||
|          <item> | ||||
|           <widget class="QFrame" name="name_frame"> | ||||
|            <property name="frameShape"> | ||||
|             <enum>QFrame::StyledPanel</enum> | ||||
|            </property> | ||||
|            <property name="frameShadow"> | ||||
|             <enum>QFrame::Raised</enum> | ||||
|            </property> | ||||
|            <layout class="QVBoxLayout" name="verticalLayout_3"> | ||||
|             <item> | ||||
|              <widget class="QLabel" name="name_label"> | ||||
|               <property name="font"> | ||||
|                <font> | ||||
|                 <weight>75</weight> | ||||
|                 <bold>true</bold> | ||||
|                </font> | ||||
|               </property> | ||||
|               <property name="text"> | ||||
|                <string>Name:</string> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|             <item> | ||||
|              <widget class="QLineEdit" name="name_line"/> | ||||
|             </item> | ||||
|            </layout> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QFrame" name="domain_frame"> | ||||
|            <property name="frameShape"> | ||||
|             <enum>QFrame::StyledPanel</enum> | ||||
|            </property> | ||||
|            <property name="frameShadow"> | ||||
|             <enum>QFrame::Raised</enum> | ||||
|            </property> | ||||
|            <layout class="QVBoxLayout" name="verticalLayout_4"> | ||||
|             <item> | ||||
|              <widget class="QLabel" name="domain_label"> | ||||
|               <property name="font"> | ||||
|                <font> | ||||
|                 <weight>75</weight> | ||||
|                 <bold>true</bold> | ||||
|                </font> | ||||
|               </property> | ||||
|               <property name="text"> | ||||
|                <string>Domain:</string> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|             <item> | ||||
|              <widget class="QLineEdit" name="domain_line"/> | ||||
|             </item> | ||||
|            </layout> | ||||
|           </widget> | ||||
|          </item> | ||||
|         </layout> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QListView" name="search_author_list"/> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="selected_presence_label"> | ||||
|         <property name="font"> | ||||
|          <font> | ||||
|           <weight>75</weight> | ||||
|           <bold>true</bold> | ||||
|          </font> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Selected Presences:</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QListView" name="selected_presence_list"/> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QFrame" name="presence_docker_button_frame"> | ||||
|         <property name="frameShape"> | ||||
|          <enum>QFrame::StyledPanel</enum> | ||||
|         </property> | ||||
|         <property name="frameShadow"> | ||||
|          <enum>QFrame::Raised</enum> | ||||
|         </property> | ||||
|         <layout class="QHBoxLayout" name="horizontalLayout"> | ||||
|          <item> | ||||
|           <widget class="QPushButton" name="create_presence_button"> | ||||
|            <property name="text"> | ||||
|             <string>Create Presence</string> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QPushButton" name="edit_presence_button"> | ||||
|            <property name="text"> | ||||
|             <string>Edit Presence</string> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QPushButton" name="delete_presence_button"> | ||||
|            <property name="text"> | ||||
|             <string>Delete Presence</string> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|         </layout> | ||||
|        </widget> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections/> | ||||
| </ui> | ||||
| @ -0,0 +1,104 @@ | ||||
| from PyQt5.QtWidgets import QMainWindow, QWidget, QLabel, QDesktopWidget, QToolBar, QPushButton, QGroupBox, QGridLayout | ||||
| from PyQt5.QtWidgets import QVBoxLayout | ||||
| from PyQt5.QtGui import QPixmap | ||||
| from PyQt5.QtCore import Qt | ||||
| 
 | ||||
| 
 | ||||
| class PictureImporter(QMainWindow): | ||||
| 
 | ||||
|     def __init__(self, parent=None, max_relative_resize_width: float = 0.8, max_relative_resize_height: float = 0.8,): | ||||
|         super().__init__(parent) | ||||
| 
 | ||||
|         # Menubar | ||||
|         self.__menu_bar = self.menuBar() | ||||
|         self.__menu_bar.addAction("Action1") | ||||
|         self.__menu_bar.addAction("Action2") | ||||
| 
 | ||||
|         # Toolbar | ||||
|         open = QToolBar() | ||||
|         open.addAction("Open") | ||||
|         self.addToolBar(open) | ||||
|         close = QToolBar() | ||||
|         close.addAction("Close") | ||||
|         self.addToolBar(close) | ||||
| 
 | ||||
|         # layout | ||||
|         self.__layout = QGridLayout() | ||||
| 
 | ||||
|         # Central Component Settings | ||||
|         self.__window = QWidget() | ||||
| 
 | ||||
|         self.setCentralWidget(self.__window) | ||||
| 
 | ||||
|         # Statusbar Settings | ||||
|         self.__status_bar = self.statusBar() | ||||
|         self.__status_bar.showMessage("Welcome to ArtNet! :)", 5000) | ||||
|         label = QLabel("ArtNet - v0.1") | ||||
|         self.__status_bar.addPermanentWidget(label) | ||||
| 
 | ||||
|         self.resize(800, 600) | ||||
|         self.setWindowTitle("ArtNet - Picture Importer") | ||||
|         self.center() | ||||
| 
 | ||||
|     def center(self): | ||||
|         """ | ||||
|         Centers the window in the middle of the screen | ||||
| 
 | ||||
|         :return: | ||||
|         """ | ||||
|         screen = QDesktopWidget().screenGeometry() | ||||
|         size = self.geometry() | ||||
|         self.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2) | ||||
| 
 | ||||
|     @DeprecationWarning | ||||
|     def display_geometry(self): | ||||
|         x = self.x() | ||||
|         y = self.y() | ||||
|         print("x: {0}, y: {1}".format(x, y)) | ||||
|         x = self.pos().x() | ||||
|         y = self.pos().y() | ||||
|         print("x: {0}, y: {1}".format(x, y)) | ||||
|         x = self.frameGeometry().x() | ||||
|         y = self.frameGeometry().y() | ||||
|         print("x: {0}, y: {1}".format(x, y)) | ||||
|         x = self.geometry().x() | ||||
|         y = self.geometry().y() | ||||
|         print("x: {0}, y: {1}".format(x, y)) | ||||
|         print("geometry: ", self.geometry()) | ||||
|         print("frameGeometry: ", self.frameGeometry()) | ||||
| 
 | ||||
|     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.__status_bar.showMessage("Welcome to ArtNet! :)", 5000) | ||||
| 
 | ||||
|     def display_image(self, full_path: str): | ||||
|         """ | ||||
|         Display an image in the central widget | ||||
|         :param full_path: | ||||
|         :return: | ||||
|         """ | ||||
|         label = QLabel(self) | ||||
|         pixmap = self.__image_resize(QPixmap(full_path)) | ||||
| 
 | ||||
|         label.setPixmap(pixmap) | ||||
|         label.setAlignment(Qt.AlignCenter) | ||||
| 
 | ||||
|         self.setCentralWidget(label) | ||||
|         self.center() | ||||
| 
 | ||||
|     def __image_resize(self, pixmap: QPixmap) -> QPixmap: | ||||
|         """ | ||||
|         Resize the given pixmap so that we're not out of the desktop. | ||||
| 
 | ||||
|         :return: new scaled QPixmap | ||||
|         """ | ||||
|         return pixmap | ||||
|         screen_rect = QDesktopWidget().screenGeometry() | ||||
|         print("Resizing pixmap to", int(screen_rect.width()*0.4), int(screen_rect.height()*0.6)) | ||||
|         return pixmap.scaled(int(screen_rect.width()*0.6), int(screen_rect.height()*0.7), Qt.KeepAspectRatio) | ||||
| 
 | ||||
| @ -0,0 +1,349 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| 
 | ||||
| # Form implementation generated from reading ui file 'picture_importer.ui' | ||||
| # | ||||
| # Created by: PyQt5 UI code generator 5.15.0 | ||||
| # | ||||
| # WARNING: Any manual changes made to this file will be lost when pyuic5 is | ||||
| # run again.  Do not edit this file unless you know what you are doing. | ||||
| 
 | ||||
| 
 | ||||
| from PyQt5 import QtCore, QtGui, QtWidgets | ||||
| 
 | ||||
| 
 | ||||
| class Ui_MainWindow(object): | ||||
|     def setupUi(self, MainWindow): | ||||
|         MainWindow.setObjectName("MainWindow") | ||||
|         MainWindow.resize(827, 777) | ||||
|         self.centralwidget = QtWidgets.QWidget(MainWindow) | ||||
|         self.centralwidget.setEnabled(True) | ||||
|         self.centralwidget.setObjectName("centralwidget") | ||||
|         self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget) | ||||
|         self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint) | ||||
|         self.horizontalLayout.setObjectName("horizontalLayout") | ||||
|         self.left_frame = QtWidgets.QFrame(self.centralwidget) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.left_frame.sizePolicy().hasHeightForWidth()) | ||||
|         self.left_frame.setSizePolicy(sizePolicy) | ||||
|         self.left_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.left_frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.left_frame.setObjectName("left_frame") | ||||
|         self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.left_frame) | ||||
|         self.horizontalLayout_2.setObjectName("horizontalLayout_2") | ||||
|         self.presence_docker_button = QtWidgets.QToolButton(self.left_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.presence_docker_button.sizePolicy().hasHeightForWidth()) | ||||
|         self.presence_docker_button.setSizePolicy(sizePolicy) | ||||
|         self.presence_docker_button.setObjectName("presence_docker_button") | ||||
|         self.horizontalLayout_2.addWidget(self.presence_docker_button) | ||||
|         self.presence_docker_layout = QtWidgets.QVBoxLayout() | ||||
|         self.presence_docker_layout.setObjectName("presence_docker_layout") | ||||
|         self.horizontalLayout_2.addLayout(self.presence_docker_layout) | ||||
|         self.left_layout = QtWidgets.QVBoxLayout() | ||||
|         self.left_layout.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize) | ||||
|         self.left_layout.setObjectName("left_layout") | ||||
|         self.prev_image_button = QtWidgets.QToolButton(self.left_frame) | ||||
|         self.prev_image_button.setObjectName("prev_image_button") | ||||
|         self.left_layout.addWidget(self.prev_image_button) | ||||
|         self.horizontalLayout_2.addLayout(self.left_layout) | ||||
|         self.center_frame = QtWidgets.QFrame(self.left_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.center_frame.sizePolicy().hasHeightForWidth()) | ||||
|         self.center_frame.setSizePolicy(sizePolicy) | ||||
|         self.center_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.center_frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.center_frame.setObjectName("center_frame") | ||||
|         self.verticalLayout = QtWidgets.QVBoxLayout(self.center_frame) | ||||
|         self.verticalLayout.setObjectName("verticalLayout") | ||||
|         self.frame = QtWidgets.QFrame(self.center_frame) | ||||
|         self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.frame.setObjectName("frame") | ||||
|         self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.frame) | ||||
|         self.horizontalLayout_4.setObjectName("horizontalLayout_4") | ||||
|         self.image_title_line = QtWidgets.QLineEdit(self.frame) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(10) | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.image_title_line.setFont(font) | ||||
|         self.image_title_line.setAutoFillBackground(False) | ||||
|         self.image_title_line.setAlignment(QtCore.Qt.AlignCenter) | ||||
|         self.image_title_line.setReadOnly(False) | ||||
|         self.image_title_line.setObjectName("image_title_line") | ||||
|         self.horizontalLayout_4.addWidget(self.image_title_line) | ||||
|         self.imageNumberSpinBox = QtWidgets.QSpinBox(self.frame) | ||||
|         self.imageNumberSpinBox.setObjectName("imageNumberSpinBox") | ||||
|         self.horizontalLayout_4.addWidget(self.imageNumberSpinBox) | ||||
|         self.verticalLayout.addWidget(self.frame) | ||||
|         self.image_file_label = QtWidgets.QLabel(self.center_frame) | ||||
|         self.image_file_label.setAlignment(QtCore.Qt.AlignCenter) | ||||
|         self.image_file_label.setObjectName("image_file_label") | ||||
|         self.verticalLayout.addWidget(self.image_file_label) | ||||
|         self.author_by = QtWidgets.QLabel(self.center_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.author_by.sizePolicy().hasHeightForWidth()) | ||||
|         self.author_by.setSizePolicy(sizePolicy) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(7) | ||||
|         font.setBold(False) | ||||
|         font.setWeight(50) | ||||
|         self.author_by.setFont(font) | ||||
|         self.author_by.setAlignment(QtCore.Qt.AlignCenter) | ||||
|         self.author_by.setObjectName("author_by") | ||||
|         self.verticalLayout.addWidget(self.author_by) | ||||
|         self.image_author_label = QtWidgets.QLabel(self.center_frame) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(10) | ||||
|         font.setBold(False) | ||||
|         font.setItalic(False) | ||||
|         font.setWeight(50) | ||||
|         self.image_author_label.setFont(font) | ||||
|         self.image_author_label.setAlignment(QtCore.Qt.AlignCenter) | ||||
|         self.image_author_label.setObjectName("image_author_label") | ||||
|         self.verticalLayout.addWidget(self.image_author_label) | ||||
|         self.image_frame = QtWidgets.QFrame(self.center_frame) | ||||
|         self.image_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.image_frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.image_frame.setObjectName("image_frame") | ||||
|         self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.image_frame) | ||||
|         self.verticalLayout_2.setObjectName("verticalLayout_2") | ||||
|         self.image_label = QtWidgets.QLabel(self.image_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.image_label.sizePolicy().hasHeightForWidth()) | ||||
|         self.image_label.setSizePolicy(sizePolicy) | ||||
|         self.image_label.setAlignment(QtCore.Qt.AlignCenter) | ||||
|         self.image_label.setObjectName("image_label") | ||||
|         self.verticalLayout_2.addWidget(self.image_label) | ||||
|         self.verticalLayout.addWidget(self.image_frame) | ||||
|         self.link_label = QtWidgets.QLabel(self.center_frame) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(8) | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.link_label.setFont(font) | ||||
|         self.link_label.setAlignment(QtCore.Qt.AlignCenter) | ||||
|         self.link_label.setObjectName("link_label") | ||||
|         self.verticalLayout.addWidget(self.link_label) | ||||
|         self.label_5 = QtWidgets.QLabel(self.center_frame) | ||||
|         self.label_5.setObjectName("label_5") | ||||
|         self.verticalLayout.addWidget(self.label_5) | ||||
|         self.link_line = QtWidgets.QLineEdit(self.center_frame) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(7) | ||||
|         self.link_line.setFont(font) | ||||
|         self.link_line.setObjectName("link_line") | ||||
|         self.verticalLayout.addWidget(self.link_line) | ||||
|         self.horizontalLayout_2.addWidget(self.center_frame) | ||||
|         self.right_layout = QtWidgets.QVBoxLayout() | ||||
|         self.right_layout.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize) | ||||
|         self.right_layout.setObjectName("right_layout") | ||||
|         self.next_image_button = QtWidgets.QToolButton(self.left_frame) | ||||
|         self.next_image_button.setObjectName("next_image_button") | ||||
|         self.right_layout.addWidget(self.next_image_button) | ||||
|         self.horizontalLayout_2.addLayout(self.right_layout) | ||||
|         self.horizontalLayout.addWidget(self.left_frame) | ||||
|         self.right_frame = QtWidgets.QFrame(self.centralwidget) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.right_frame.sizePolicy().hasHeightForWidth()) | ||||
|         self.right_frame.setSizePolicy(sizePolicy) | ||||
|         self.right_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) | ||||
|         self.right_frame.setFrameShadow(QtWidgets.QFrame.Raised) | ||||
|         self.right_frame.setObjectName("right_frame") | ||||
|         self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.right_frame) | ||||
|         self.horizontalLayout_3.setObjectName("horizontalLayout_3") | ||||
|         self.tag_layout = QtWidgets.QVBoxLayout() | ||||
|         self.tag_layout.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize) | ||||
|         self.tag_layout.setObjectName("tag_layout") | ||||
|         self.label = QtWidgets.QLabel(self.right_frame) | ||||
|         font = QtGui.QFont() | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.label.setFont(font) | ||||
|         self.label.setObjectName("label") | ||||
|         self.tag_layout.addWidget(self.label) | ||||
|         self.tag_list_layout = QtWidgets.QVBoxLayout() | ||||
|         self.tag_list_layout.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize) | ||||
|         self.tag_list_layout.setObjectName("tag_list_layout") | ||||
|         self.tag_search_bar = QtWidgets.QLineEdit(self.right_frame) | ||||
|         self.tag_search_bar.setMaximumSize(QtCore.QSize(400, 16777215)) | ||||
|         self.tag_search_bar.setObjectName("tag_search_bar") | ||||
|         self.tag_list_layout.addWidget(self.tag_search_bar) | ||||
|         self.search_result_list = QtWidgets.QListView(self.right_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.search_result_list.sizePolicy().hasHeightForWidth()) | ||||
|         self.search_result_list.setSizePolicy(sizePolicy) | ||||
|         self.search_result_list.setMinimumSize(QtCore.QSize(0, 0)) | ||||
|         self.search_result_list.setMaximumSize(QtCore.QSize(400, 16777215)) | ||||
|         self.search_result_list.setObjectName("search_result_list") | ||||
|         self.tag_list_layout.addWidget(self.search_result_list) | ||||
|         self.label_2 = QtWidgets.QLabel(self.right_frame) | ||||
|         font = QtGui.QFont() | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.label_2.setFont(font) | ||||
|         self.label_2.setObjectName("label_2") | ||||
|         self.tag_list_layout.addWidget(self.label_2) | ||||
|         self.tag_list = QtWidgets.QListView(self.right_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.tag_list.sizePolicy().hasHeightForWidth()) | ||||
|         self.tag_list.setSizePolicy(sizePolicy) | ||||
|         self.tag_list.setMinimumSize(QtCore.QSize(100, 0)) | ||||
|         self.tag_list.setMaximumSize(QtCore.QSize(400, 16777215)) | ||||
|         self.tag_list.setObjectName("tag_list") | ||||
|         self.tag_list_layout.addWidget(self.tag_list) | ||||
|         self.label_3 = QtWidgets.QLabel(self.right_frame) | ||||
|         font = QtGui.QFont() | ||||
|         font.setPointSize(9) | ||||
|         font.setBold(True) | ||||
|         font.setWeight(75) | ||||
|         self.label_3.setFont(font) | ||||
|         self.label_3.setObjectName("label_3") | ||||
|         self.tag_list_layout.addWidget(self.label_3) | ||||
|         self.implied_tag_list = QtWidgets.QListView(self.right_frame) | ||||
|         self.implied_tag_list.setMinimumSize(QtCore.QSize(100, 0)) | ||||
|         self.implied_tag_list.setMaximumSize(QtCore.QSize(400, 16777215)) | ||||
|         self.implied_tag_list.setObjectName("implied_tag_list") | ||||
|         self.tag_list_layout.addWidget(self.implied_tag_list) | ||||
|         self.tag_layout.addLayout(self.tag_list_layout) | ||||
|         self.tag_button_layout = QtWidgets.QHBoxLayout() | ||||
|         self.tag_button_layout.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize) | ||||
|         self.tag_button_layout.setSpacing(5) | ||||
|         self.tag_button_layout.setObjectName("tag_button_layout") | ||||
|         self.save_button = QtWidgets.QPushButton(self.right_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.save_button.sizePolicy().hasHeightForWidth()) | ||||
|         self.save_button.setSizePolicy(sizePolicy) | ||||
|         self.save_button.setMaximumSize(QtCore.QSize(16777215, 16777215)) | ||||
|         self.save_button.setObjectName("save_button") | ||||
|         self.tag_button_layout.addWidget(self.save_button) | ||||
|         self.import_button = QtWidgets.QPushButton(self.right_frame) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.import_button.sizePolicy().hasHeightForWidth()) | ||||
|         self.import_button.setSizePolicy(sizePolicy) | ||||
|         self.import_button.setMaximumSize(QtCore.QSize(16777215, 16777215)) | ||||
|         self.import_button.setObjectName("import_button") | ||||
|         self.tag_button_layout.addWidget(self.import_button) | ||||
|         self.prev_unknown_image_button = QtWidgets.QPushButton(self.right_frame) | ||||
|         self.prev_unknown_image_button.setObjectName("prev_unknown_image_button") | ||||
|         self.tag_button_layout.addWidget(self.prev_unknown_image_button) | ||||
|         self.next_unknown_image_button = QtWidgets.QPushButton(self.right_frame) | ||||
|         self.next_unknown_image_button.setObjectName("next_unknown_image_button") | ||||
|         self.tag_button_layout.addWidget(self.next_unknown_image_button) | ||||
|         self.delete_button = QtWidgets.QPushButton(self.right_frame) | ||||
|         self.delete_button.setObjectName("delete_button") | ||||
|         self.tag_button_layout.addWidget(self.delete_button) | ||||
|         spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) | ||||
|         self.tag_button_layout.addItem(spacerItem) | ||||
|         self.tag_layout.addLayout(self.tag_button_layout) | ||||
|         self.horizontalLayout_3.addLayout(self.tag_layout) | ||||
|         self.horizontalLayout.addWidget(self.right_frame) | ||||
|         MainWindow.setCentralWidget(self.centralwidget) | ||||
|         self.statusbar = QtWidgets.QStatusBar(MainWindow) | ||||
|         self.statusbar.setObjectName("statusbar") | ||||
|         MainWindow.setStatusBar(self.statusbar) | ||||
|         self.menuBar = QtWidgets.QMenuBar(MainWindow) | ||||
|         self.menuBar.setGeometry(QtCore.QRect(0, 0, 827, 19)) | ||||
|         self.menuBar.setObjectName("menuBar") | ||||
|         self.menuArtNet = QtWidgets.QMenu(self.menuBar) | ||||
|         self.menuArtNet.setObjectName("menuArtNet") | ||||
|         self.menuTags = QtWidgets.QMenu(self.menuBar) | ||||
|         self.menuTags.setObjectName("menuTags") | ||||
|         self.menuCategory = QtWidgets.QMenu(self.menuBar) | ||||
|         self.menuCategory.setObjectName("menuCategory") | ||||
|         MainWindow.setMenuBar(self.menuBar) | ||||
|         self.actionChange_Connection_Details = QtWidgets.QAction(MainWindow) | ||||
|         self.actionChange_Connection_Details.setObjectName("actionChange_Connection_Details") | ||||
|         self.actionChange_ArtNet_Root_Folder = QtWidgets.QAction(MainWindow) | ||||
|         self.actionChange_ArtNet_Root_Folder.setObjectName("actionChange_ArtNet_Root_Folder") | ||||
|         self.actionCreate_New_Tag = QtWidgets.QAction(MainWindow) | ||||
|         self.actionCreate_New_Tag.setObjectName("actionCreate_New_Tag") | ||||
|         self.actionDelete_a_Tag = QtWidgets.QAction(MainWindow) | ||||
|         self.actionDelete_a_Tag.setObjectName("actionDelete_a_Tag") | ||||
|         self.actionEdit_a_Tag = QtWidgets.QAction(MainWindow) | ||||
|         self.actionEdit_a_Tag.setObjectName("actionEdit_a_Tag") | ||||
|         self.actionCreate_New_Category = QtWidgets.QAction(MainWindow) | ||||
|         self.actionCreate_New_Category.setObjectName("actionCreate_New_Category") | ||||
|         self.actionDelete_a_Category = QtWidgets.QAction(MainWindow) | ||||
|         self.actionDelete_a_Category.setObjectName("actionDelete_a_Category") | ||||
|         self.actionCreate_New_Category_2 = QtWidgets.QAction(MainWindow) | ||||
|         self.actionCreate_New_Category_2.setObjectName("actionCreate_New_Category_2") | ||||
|         self.actionDelete_a_Category_2 = QtWidgets.QAction(MainWindow) | ||||
|         self.actionDelete_a_Category_2.setObjectName("actionDelete_a_Category_2") | ||||
|         self.menuArtNet.addAction(self.actionChange_Connection_Details) | ||||
|         self.menuArtNet.addAction(self.actionChange_ArtNet_Root_Folder) | ||||
|         self.menuTags.addAction(self.actionCreate_New_Tag) | ||||
|         self.menuTags.addAction(self.actionDelete_a_Tag) | ||||
|         self.menuTags.addAction(self.actionEdit_a_Tag) | ||||
|         self.menuCategory.addAction(self.actionCreate_New_Category_2) | ||||
|         self.menuCategory.addAction(self.actionDelete_a_Category_2) | ||||
|         self.menuBar.addAction(self.menuArtNet.menuAction()) | ||||
|         self.menuBar.addAction(self.menuTags.menuAction()) | ||||
|         self.menuBar.addAction(self.menuCategory.menuAction()) | ||||
| 
 | ||||
|         self.retranslateUi(MainWindow) | ||||
|         QtCore.QMetaObject.connectSlotsByName(MainWindow) | ||||
| 
 | ||||
|     def retranslateUi(self, MainWindow): | ||||
|         _translate = QtCore.QCoreApplication.translate | ||||
|         MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) | ||||
|         self.presence_docker_button.setText(_translate("MainWindow", "<")) | ||||
|         self.prev_image_button.setText(_translate("MainWindow", "<")) | ||||
|         self.image_title_line.setText(_translate("MainWindow", "Image_Title")) | ||||
|         self.image_file_label.setText(_translate("MainWindow", "file_name")) | ||||
|         self.author_by.setText(_translate("MainWindow", "by")) | ||||
|         self.image_author_label.setText(_translate("MainWindow", "author_name")) | ||||
|         self.image_label.setText(_translate("MainWindow", "No Image")) | ||||
|         self.link_label.setText(_translate("MainWindow", "Source_Link")) | ||||
|         self.label_5.setText(_translate("MainWindow", "Link:")) | ||||
|         self.next_image_button.setText(_translate("MainWindow", ">")) | ||||
|         self.label.setText(_translate("MainWindow", "Search Tags")) | ||||
|         self.label_2.setText(_translate("MainWindow", "Selected Tags:")) | ||||
|         self.label_3.setText(_translate("MainWindow", "Implied Tags:")) | ||||
|         self.save_button.setText(_translate("MainWindow", "Save")) | ||||
|         self.import_button.setText(_translate("MainWindow", "Import Tags")) | ||||
|         self.prev_unknown_image_button.setText(_translate("MainWindow", "prev Unknown")) | ||||
|         self.next_unknown_image_button.setText(_translate("MainWindow", "next Unknown")) | ||||
|         self.delete_button.setText(_translate("MainWindow", "Delete")) | ||||
|         self.menuArtNet.setTitle(_translate("MainWindow", "ArtNet")) | ||||
|         self.menuTags.setTitle(_translate("MainWindow", "Tags")) | ||||
|         self.menuCategory.setTitle(_translate("MainWindow", "Category")) | ||||
|         self.actionChange_Connection_Details.setText(_translate("MainWindow", "Change DB Connection")) | ||||
|         self.actionChange_ArtNet_Root_Folder.setText(_translate("MainWindow", "Change ArtNet Root")) | ||||
|         self.actionCreate_New_Tag.setText(_translate("MainWindow", "Create New Tag")) | ||||
|         self.actionDelete_a_Tag.setText(_translate("MainWindow", "Delete a Tag")) | ||||
|         self.actionEdit_a_Tag.setText(_translate("MainWindow", "Edit a Tag")) | ||||
|         self.actionCreate_New_Category.setText(_translate("MainWindow", "Create New Category")) | ||||
|         self.actionDelete_a_Category.setText(_translate("MainWindow", "Delete a Category")) | ||||
|         self.actionCreate_New_Category_2.setText(_translate("MainWindow", "Create New Category")) | ||||
|         self.actionDelete_a_Category_2.setText(_translate("MainWindow", "Delete a Category")) | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     import sys | ||||
|     app = QtWidgets.QApplication(sys.argv) | ||||
|     MainWindow = QtWidgets.QMainWindow() | ||||
|     ui = Ui_MainWindow() | ||||
|     ui.setupUi(MainWindow) | ||||
|     MainWindow.show() | ||||
|     sys.exit(app.exec_()) | ||||
| @ -0,0 +1,564 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>MainWindow</class> | ||||
|  <widget class="QMainWindow" name="MainWindow"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>827</width> | ||||
|     <height>777</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>MainWindow</string> | ||||
|   </property> | ||||
|   <widget class="QWidget" name="centralwidget"> | ||||
|    <property name="enabled"> | ||||
|     <bool>true</bool> | ||||
|    </property> | ||||
|    <layout class="QHBoxLayout" name="horizontalLayout"> | ||||
|     <property name="sizeConstraint"> | ||||
|      <enum>QLayout::SetDefaultConstraint</enum> | ||||
|     </property> | ||||
|     <item> | ||||
|      <widget class="QFrame" name="left_frame"> | ||||
|       <property name="sizePolicy"> | ||||
|        <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> | ||||
|         <horstretch>0</horstretch> | ||||
|         <verstretch>0</verstretch> | ||||
|        </sizepolicy> | ||||
|       </property> | ||||
|       <property name="frameShape"> | ||||
|        <enum>QFrame::StyledPanel</enum> | ||||
|       </property> | ||||
|       <property name="frameShadow"> | ||||
|        <enum>QFrame::Raised</enum> | ||||
|       </property> | ||||
|       <layout class="QHBoxLayout" name="horizontalLayout_2"> | ||||
|        <item> | ||||
|         <widget class="QToolButton" name="presence_docker_button"> | ||||
|          <property name="sizePolicy"> | ||||
|           <sizepolicy hsizetype="Fixed" vsizetype="Expanding"> | ||||
|            <horstretch>0</horstretch> | ||||
|            <verstretch>0</verstretch> | ||||
|           </sizepolicy> | ||||
|          </property> | ||||
|          <property name="text"> | ||||
|           <string><</string> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item> | ||||
|         <layout class="QVBoxLayout" name="presence_docker_layout"/> | ||||
|        </item> | ||||
|        <item> | ||||
|         <layout class="QVBoxLayout" name="left_layout"> | ||||
|          <property name="sizeConstraint"> | ||||
|           <enum>QLayout::SetMinimumSize</enum> | ||||
|          </property> | ||||
|          <item> | ||||
|           <widget class="QToolButton" name="prev_image_button"> | ||||
|            <property name="text"> | ||||
|             <string><</string> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|         </layout> | ||||
|        </item> | ||||
|        <item> | ||||
|         <widget class="QFrame" name="center_frame"> | ||||
|          <property name="sizePolicy"> | ||||
|           <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> | ||||
|            <horstretch>0</horstretch> | ||||
|            <verstretch>0</verstretch> | ||||
|           </sizepolicy> | ||||
|          </property> | ||||
|          <property name="frameShape"> | ||||
|           <enum>QFrame::StyledPanel</enum> | ||||
|          </property> | ||||
|          <property name="frameShadow"> | ||||
|           <enum>QFrame::Raised</enum> | ||||
|          </property> | ||||
|          <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|           <item> | ||||
|            <widget class="QFrame" name="frame"> | ||||
|             <property name="frameShape"> | ||||
|              <enum>QFrame::StyledPanel</enum> | ||||
|             </property> | ||||
|             <property name="frameShadow"> | ||||
|              <enum>QFrame::Raised</enum> | ||||
|             </property> | ||||
|             <layout class="QHBoxLayout" name="horizontalLayout_4"> | ||||
|              <item> | ||||
|               <widget class="QLineEdit" name="image_title_line"> | ||||
|                <property name="font"> | ||||
|                 <font> | ||||
|                  <pointsize>10</pointsize> | ||||
|                  <weight>75</weight> | ||||
|                  <bold>true</bold> | ||||
|                 </font> | ||||
|                </property> | ||||
|                <property name="autoFillBackground"> | ||||
|                 <bool>false</bool> | ||||
|                </property> | ||||
|                <property name="text"> | ||||
|                 <string>Image_Title</string> | ||||
|                </property> | ||||
|                <property name="alignment"> | ||||
|                 <set>Qt::AlignCenter</set> | ||||
|                </property> | ||||
|                <property name="readOnly"> | ||||
|                 <bool>false</bool> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item> | ||||
|               <widget class="QSpinBox" name="imageNumberSpinBox"/> | ||||
|              </item> | ||||
|             </layout> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <widget class="QLabel" name="image_file_label"> | ||||
|             <property name="text"> | ||||
|              <string>file_name</string> | ||||
|             </property> | ||||
|             <property name="alignment"> | ||||
|              <set>Qt::AlignCenter</set> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <widget class="QLabel" name="author_by"> | ||||
|             <property name="sizePolicy"> | ||||
|              <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> | ||||
|               <horstretch>0</horstretch> | ||||
|               <verstretch>0</verstretch> | ||||
|              </sizepolicy> | ||||
|             </property> | ||||
|             <property name="font"> | ||||
|              <font> | ||||
|               <pointsize>7</pointsize> | ||||
|               <weight>50</weight> | ||||
|               <bold>false</bold> | ||||
|              </font> | ||||
|             </property> | ||||
|             <property name="text"> | ||||
|              <string>by</string> | ||||
|             </property> | ||||
|             <property name="alignment"> | ||||
|              <set>Qt::AlignCenter</set> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <widget class="QLabel" name="image_author_label"> | ||||
|             <property name="font"> | ||||
|              <font> | ||||
|               <pointsize>10</pointsize> | ||||
|               <weight>50</weight> | ||||
|               <italic>false</italic> | ||||
|               <bold>false</bold> | ||||
|              </font> | ||||
|             </property> | ||||
|             <property name="text"> | ||||
|              <string>author_name</string> | ||||
|             </property> | ||||
|             <property name="alignment"> | ||||
|              <set>Qt::AlignCenter</set> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <widget class="QFrame" name="image_frame"> | ||||
|             <property name="frameShape"> | ||||
|              <enum>QFrame::StyledPanel</enum> | ||||
|             </property> | ||||
|             <property name="frameShadow"> | ||||
|              <enum>QFrame::Raised</enum> | ||||
|             </property> | ||||
|             <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||
|              <item> | ||||
|               <widget class="QLabel" name="image_label"> | ||||
|                <property name="sizePolicy"> | ||||
|                 <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> | ||||
|                  <horstretch>0</horstretch> | ||||
|                  <verstretch>0</verstretch> | ||||
|                 </sizepolicy> | ||||
|                </property> | ||||
|                <property name="text"> | ||||
|                 <string>No Image</string> | ||||
|                </property> | ||||
|                <property name="alignment"> | ||||
|                 <set>Qt::AlignCenter</set> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|             </layout> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <widget class="QLabel" name="link_label"> | ||||
|             <property name="font"> | ||||
|              <font> | ||||
|               <pointsize>8</pointsize> | ||||
|               <weight>75</weight> | ||||
|               <bold>true</bold> | ||||
|              </font> | ||||
|             </property> | ||||
|             <property name="text"> | ||||
|              <string>Source_Link</string> | ||||
|             </property> | ||||
|             <property name="alignment"> | ||||
|              <set>Qt::AlignCenter</set> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <widget class="QLabel" name="label_5"> | ||||
|             <property name="text"> | ||||
|              <string>Link:</string> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <widget class="QLineEdit" name="link_line"> | ||||
|             <property name="font"> | ||||
|              <font> | ||||
|               <pointsize>7</pointsize> | ||||
|              </font> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|          </layout> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item> | ||||
|         <layout class="QVBoxLayout" name="right_layout"> | ||||
|          <property name="sizeConstraint"> | ||||
|           <enum>QLayout::SetMinimumSize</enum> | ||||
|          </property> | ||||
|          <item> | ||||
|           <widget class="QToolButton" name="next_image_button"> | ||||
|            <property name="text"> | ||||
|             <string>></string> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|         </layout> | ||||
|        </item> | ||||
|       </layout> | ||||
|      </widget> | ||||
|     </item> | ||||
|     <item> | ||||
|      <widget class="QFrame" name="right_frame"> | ||||
|       <property name="sizePolicy"> | ||||
|        <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> | ||||
|         <horstretch>0</horstretch> | ||||
|         <verstretch>0</verstretch> | ||||
|        </sizepolicy> | ||||
|       </property> | ||||
|       <property name="frameShape"> | ||||
|        <enum>QFrame::StyledPanel</enum> | ||||
|       </property> | ||||
|       <property name="frameShadow"> | ||||
|        <enum>QFrame::Raised</enum> | ||||
|       </property> | ||||
|       <layout class="QHBoxLayout" name="horizontalLayout_3"> | ||||
|        <item> | ||||
|         <layout class="QVBoxLayout" name="tag_layout"> | ||||
|          <property name="sizeConstraint"> | ||||
|           <enum>QLayout::SetMinimumSize</enum> | ||||
|          </property> | ||||
|          <item> | ||||
|           <widget class="QLabel" name="label"> | ||||
|            <property name="font"> | ||||
|             <font> | ||||
|              <weight>75</weight> | ||||
|              <bold>true</bold> | ||||
|             </font> | ||||
|            </property> | ||||
|            <property name="text"> | ||||
|             <string>Search Tags</string> | ||||
|            </property> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <layout class="QVBoxLayout" name="tag_list_layout"> | ||||
|            <property name="sizeConstraint"> | ||||
|             <enum>QLayout::SetMinimumSize</enum> | ||||
|            </property> | ||||
|            <item> | ||||
|             <widget class="QLineEdit" name="tag_search_bar"> | ||||
|              <property name="maximumSize"> | ||||
|               <size> | ||||
|                <width>400</width> | ||||
|                <height>16777215</height> | ||||
|               </size> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QListView" name="search_result_list"> | ||||
|              <property name="sizePolicy"> | ||||
|               <sizepolicy hsizetype="Expanding" vsizetype="Minimum"> | ||||
|                <horstretch>0</horstretch> | ||||
|                <verstretch>0</verstretch> | ||||
|               </sizepolicy> | ||||
|              </property> | ||||
|              <property name="minimumSize"> | ||||
|               <size> | ||||
|                <width>0</width> | ||||
|                <height>0</height> | ||||
|               </size> | ||||
|              </property> | ||||
|              <property name="maximumSize"> | ||||
|               <size> | ||||
|                <width>400</width> | ||||
|                <height>16777215</height> | ||||
|               </size> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QLabel" name="label_2"> | ||||
|              <property name="font"> | ||||
|               <font> | ||||
|                <weight>75</weight> | ||||
|                <bold>true</bold> | ||||
|               </font> | ||||
|              </property> | ||||
|              <property name="text"> | ||||
|               <string>Selected Tags:</string> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QListView" name="tag_list"> | ||||
|              <property name="sizePolicy"> | ||||
|               <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> | ||||
|                <horstretch>0</horstretch> | ||||
|                <verstretch>0</verstretch> | ||||
|               </sizepolicy> | ||||
|              </property> | ||||
|              <property name="minimumSize"> | ||||
|               <size> | ||||
|                <width>100</width> | ||||
|                <height>0</height> | ||||
|               </size> | ||||
|              </property> | ||||
|              <property name="maximumSize"> | ||||
|               <size> | ||||
|                <width>400</width> | ||||
|                <height>16777215</height> | ||||
|               </size> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QLabel" name="label_3"> | ||||
|              <property name="font"> | ||||
|               <font> | ||||
|                <pointsize>9</pointsize> | ||||
|                <weight>75</weight> | ||||
|                <bold>true</bold> | ||||
|               </font> | ||||
|              </property> | ||||
|              <property name="text"> | ||||
|               <string>Implied Tags:</string> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QListView" name="implied_tag_list"> | ||||
|              <property name="minimumSize"> | ||||
|               <size> | ||||
|                <width>100</width> | ||||
|                <height>0</height> | ||||
|               </size> | ||||
|              </property> | ||||
|              <property name="maximumSize"> | ||||
|               <size> | ||||
|                <width>400</width> | ||||
|                <height>16777215</height> | ||||
|               </size> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|           </layout> | ||||
|          </item> | ||||
|          <item> | ||||
|           <layout class="QHBoxLayout" name="tag_button_layout"> | ||||
|            <property name="spacing"> | ||||
|             <number>5</number> | ||||
|            </property> | ||||
|            <property name="sizeConstraint"> | ||||
|             <enum>QLayout::SetMinimumSize</enum> | ||||
|            </property> | ||||
|            <item> | ||||
|             <widget class="QPushButton" name="save_button"> | ||||
|              <property name="sizePolicy"> | ||||
|               <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> | ||||
|                <horstretch>0</horstretch> | ||||
|                <verstretch>0</verstretch> | ||||
|               </sizepolicy> | ||||
|              </property> | ||||
|              <property name="maximumSize"> | ||||
|               <size> | ||||
|                <width>16777215</width> | ||||
|                <height>16777215</height> | ||||
|               </size> | ||||
|              </property> | ||||
|              <property name="text"> | ||||
|               <string>Save</string> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QPushButton" name="import_button"> | ||||
|              <property name="sizePolicy"> | ||||
|               <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> | ||||
|                <horstretch>0</horstretch> | ||||
|                <verstretch>0</verstretch> | ||||
|               </sizepolicy> | ||||
|              </property> | ||||
|              <property name="maximumSize"> | ||||
|               <size> | ||||
|                <width>16777215</width> | ||||
|                <height>16777215</height> | ||||
|               </size> | ||||
|              </property> | ||||
|              <property name="text"> | ||||
|               <string>Import Tags</string> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QPushButton" name="prev_unknown_image_button"> | ||||
|              <property name="text"> | ||||
|               <string>prev Unknown</string> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QPushButton" name="next_unknown_image_button"> | ||||
|              <property name="text"> | ||||
|               <string>next Unknown</string> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QPushButton" name="delete_button"> | ||||
|              <property name="text"> | ||||
|               <string>Delete</string> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <spacer name="horizontalSpacer"> | ||||
|              <property name="orientation"> | ||||
|               <enum>Qt::Horizontal</enum> | ||||
|              </property> | ||||
|              <property name="sizeHint" stdset="0"> | ||||
|               <size> | ||||
|                <width>40</width> | ||||
|                <height>20</height> | ||||
|               </size> | ||||
|              </property> | ||||
|             </spacer> | ||||
|            </item> | ||||
|           </layout> | ||||
|          </item> | ||||
|         </layout> | ||||
|        </item> | ||||
|       </layout> | ||||
|      </widget> | ||||
|     </item> | ||||
|    </layout> | ||||
|   </widget> | ||||
|   <widget class="QStatusBar" name="statusbar"/> | ||||
|   <widget class="QMenuBar" name="menuBar"> | ||||
|    <property name="geometry"> | ||||
|     <rect> | ||||
|      <x>0</x> | ||||
|      <y>0</y> | ||||
|      <width>827</width> | ||||
|      <height>19</height> | ||||
|     </rect> | ||||
|    </property> | ||||
|    <widget class="QMenu" name="menuArtNet"> | ||||
|     <property name="title"> | ||||
|      <string>ArtNet</string> | ||||
|     </property> | ||||
|     <addaction name="actionChange_Connection_Details"/> | ||||
|     <addaction name="actionChange_ArtNet_Root_Folder"/> | ||||
|    </widget> | ||||
|    <widget class="QMenu" name="menuTags"> | ||||
|     <property name="title"> | ||||
|      <string>Tags</string> | ||||
|     </property> | ||||
|     <addaction name="actionCreate_New_Tag"/> | ||||
|     <addaction name="actionDelete_a_Tag"/> | ||||
|     <addaction name="actionEdit_a_Tag"/> | ||||
|    </widget> | ||||
|    <widget class="QMenu" name="menuCategory"> | ||||
|     <property name="title"> | ||||
|      <string>Category</string> | ||||
|     </property> | ||||
|     <addaction name="actionCreate_New_Category_2"/> | ||||
|     <addaction name="actionDelete_a_Category_2"/> | ||||
|    </widget> | ||||
|    <addaction name="menuArtNet"/> | ||||
|    <addaction name="menuTags"/> | ||||
|    <addaction name="menuCategory"/> | ||||
|   </widget> | ||||
|   <action name="actionChange_Connection_Details"> | ||||
|    <property name="text"> | ||||
|     <string>Change DB Connection</string> | ||||
|    </property> | ||||
|   </action> | ||||
|   <action name="actionChange_ArtNet_Root_Folder"> | ||||
|    <property name="text"> | ||||
|     <string>Change ArtNet Root</string> | ||||
|    </property> | ||||
|   </action> | ||||
|   <action name="actionCreate_New_Tag"> | ||||
|    <property name="text"> | ||||
|     <string>Create New Tag</string> | ||||
|    </property> | ||||
|   </action> | ||||
|   <action name="actionDelete_a_Tag"> | ||||
|    <property name="text"> | ||||
|     <string>Delete a Tag</string> | ||||
|    </property> | ||||
|   </action> | ||||
|   <action name="actionEdit_a_Tag"> | ||||
|    <property name="text"> | ||||
|     <string>Edit a Tag</string> | ||||
|    </property> | ||||
|   </action> | ||||
|   <action name="actionCreate_New_Category"> | ||||
|    <property name="text"> | ||||
|     <string>Create New Category</string> | ||||
|    </property> | ||||
|   </action> | ||||
|   <action name="actionDelete_a_Category"> | ||||
|    <property name="text"> | ||||
|     <string>Delete a Category</string> | ||||
|    </property> | ||||
|   </action> | ||||
|   <action name="actionCreate_New_Category_2"> | ||||
|    <property name="text"> | ||||
|     <string>Create New Category</string> | ||||
|    </property> | ||||
|   </action> | ||||
|   <action name="actionDelete_a_Category_2"> | ||||
|    <property name="text"> | ||||
|     <string>Delete a Category</string> | ||||
|    </property> | ||||
|   </action> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections/> | ||||
| </ui> | ||||
| @ -0,0 +1,999 @@ | ||||
| import validators, os | ||||
| 
 | ||||
| from PyQt5 import QtWidgets | ||||
| from PyQt5.QtCore import Qt, QSize, QUrl | ||||
| from PyQt5.QtGui import QPixmap, QResizeEvent, QKeyEvent, QStandardItemModel, QStandardItem, QMovie | ||||
| from PyQt5 import QtMultimedia | ||||
| from PyQt5.QtMultimediaWidgets import QVideoWidget | ||||
| 
 | ||||
| from ArtNet.gui.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): | ||||
| 
 | ||||
|     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.__showing_video: 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.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.link_label.setText("No Source Available") | ||||
|         self.ui.image_file_label.setTextInteractionFlags(Qt.TextSelectableByMouse) | ||||
|         self.set_image_title_link() | ||||
| 
 | ||||
|         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((screen.width() - size.width()) / 3, (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: | ||||
|         """ | ||||
|         image_data = { | ||||
|             "ID": self.curr_art_id, | ||||
|             "title": self.curr_image_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) | ||||
|         } | ||||
| 
 | ||||
|         for presence in self.curr_presences: | ||||
|             if presence[-1] == "(Not in Database)": | ||||
|                 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"]) | ||||
| 
 | ||||
|         self.set_temporary_status_message("Saved {0} ({1}) to ArtNet DB!" | ||||
|                                           .format(self.curr_image_title, self.curr_art_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 remove_presence(self, name: str, domain: str): | ||||
|         """ | ||||
|         Remove the presence from the DB | ||||
|         :param name: | ||||
|         :param domain: | ||||
|         :return: | ||||
|         """ | ||||
|         self.__main.db_connection.remove_presence(name, domain) | ||||
| 
 | ||||
|     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 == "(Not in Database)": | ||||
|                     presences.remove((name, domain)) | ||||
|         elif len(presences) == 0: | ||||
|             presences = [(self.curr_art_path.split("/")[0], "(Not in Database)")] | ||||
|         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(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_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: | ||||
|                 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_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(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(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): | ||||
|         """ | ||||
|         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 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"]: | ||||
|             self.__showing_video = True | ||||
|             self.__video = QVideoWidget() | ||||
|             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) | ||||
|         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.set_image_title_link() | ||||
|         self.set_image_id_spinbox() | ||||
| 
 | ||||
|     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: | ||||
|             print("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) | ||||
| 
 | ||||
|     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): | ||||
|         print("Clicked Save!") | ||||
|         self.set_image_title_link() | ||||
|         self.save_changes() | ||||
| 
 | ||||
|     def on_import_tags_clicked(self): | ||||
|         print("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(self.curr_art_id, result) | ||||
|         self.set_tag_list(self.curr_tags) | ||||
| 
 | ||||
|     def on_next_clicked(self): | ||||
|         print("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.change_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): | ||||
|         print("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.change_image() | ||||
| 
 | ||||
|         if self.presence_docker_open: | ||||
|             self.toggle_presence_docker() | ||||
|         self.on_tag_search_change() | ||||
| 
 | ||||
|     def toggle_presence_docker(self): | ||||
|         print("Clicked presence docker button!") | ||||
|         if not self.presence_docker_open: | ||||
|             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: | ||||
|             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): | ||||
|         print("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): | ||||
|         print("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): | ||||
|         print("Clicked Tag Creation!") | ||||
|         dialog = TagModifyDialog(self, create_tag=True) | ||||
| 
 | ||||
|         tag_data: dict = dialog.exec_() | ||||
|         print("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): | ||||
|         print("Clicked Tag Deletion!") | ||||
|         dialog = TagSelectDialog(self, delete_tag=True) | ||||
| 
 | ||||
|         tag = dialog.exec_() | ||||
|         print("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(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_() | ||||
|         print("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): | ||||
|         print("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(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 = tag["category"] | ||||
|         edit_dialog.set_all_categories() | ||||
| 
 | ||||
|         tag_data = edit_dialog.exec_() | ||||
|         print("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(name=tag_data["name"], description=tag_data["description"], | ||||
|                                            aliases=tag_data["aliases"], implications=tag_data["implications"], | ||||
|                                            category=tag_data["category"], old_tag=tag_data["old_tag_name"]) | ||||
|         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): | ||||
|         print("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_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() | ||||
|         print("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.change_image() | ||||
| 
 | ||||
|     def on_next_unknown_image_clicked(self): | ||||
|         unknown_image_index = self.__main.get_next_unknown_image() | ||||
|         print("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.change_image() | ||||
| 
 | ||||
|     def on_image_id_spinbox_changed(self, v: int): | ||||
|         if self.__tmp_imageid_spinbox == v: | ||||
|             print("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.change_image() | ||||
| 
 | ||||
|         self.__tmp_imageid_spinbox: int = v | ||||
| 
 | ||||
|     def on_delete_image_clicked(self): | ||||
|         print("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: | ||||
|             print("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: | ||||
|                 print(f"deleting image data of \"{self.curr_image_title}\"") | ||||
|                 self.__main.db_connection.remove_image(art_hash) | ||||
|             else: | ||||
|                 return | ||||
| 
 | ||||
|         else: | ||||
|             print("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: | ||||
|                 print("deleting image file") | ||||
|                 self.__main.delete_image(self.curr_art_path) | ||||
|             else: | ||||
|                 return | ||||
| 
 | ||||
|         self.__main.change_image() | ||||
| @ -0,0 +1,169 @@ | ||||
| import re | ||||
| import requests | ||||
| import lxml.html | ||||
| from urllib.parse import urlparse | ||||
| 
 | ||||
| DOMAIN_UNKNOWN = -1 | ||||
| 
 | ||||
| 
 | ||||
| class DomainIdentifier: | ||||
|     """ | ||||
|     Makeshift-automated enum class to allow runtime expansion of an enum. | ||||
| 
 | ||||
|     Create an instance to get a new unique domain identifier that can resolve to an int. | ||||
| 
 | ||||
|     TODO find better way :D | ||||
|     """ | ||||
| 
 | ||||
|     __domain_list = [] | ||||
| 
 | ||||
|     def __init__(self, name: str): | ||||
|         self.__name = name | ||||
|         self.__domain_number = len(DomainIdentifier.__domain_list) | ||||
| 
 | ||||
|         if DomainIdentifier.get_identifier(name) is not None: | ||||
|             raise Exception("The domain \"{0}\" is already in the list! Tried to create a duplicate!".format(name)) | ||||
| 
 | ||||
|         DomainIdentifier.__domain_list.append(self) | ||||
| 
 | ||||
|     @property | ||||
|     def name(self) -> str: | ||||
|         return self.__name | ||||
| 
 | ||||
|     @property | ||||
|     def identifier(self) -> int: | ||||
|         return self.__domain_number | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def get_identifier(name: str) -> "DomainIdentifier": | ||||
|         for d in DomainIdentifier.__domain_list: | ||||
|             if d.name == name: | ||||
|                 return d | ||||
|         return None | ||||
| 
 | ||||
| 
 | ||||
| class DomainLinkGenerator: | ||||
| 
 | ||||
|     def __init__(self, domain: DomainIdentifier): | ||||
|         self.__identifier = domain | ||||
| 
 | ||||
|     def match_file_name(self, file_name) -> bool: | ||||
|         """ | ||||
|         Checks if a given file name is plausible to be used by the given domain | ||||
|         :param file_name: | ||||
|         :return: | ||||
|         """ | ||||
|         raise NotImplementedError | ||||
| 
 | ||||
|     def get_domain_name(self) -> str: | ||||
|         return self.__identifier.name | ||||
| 
 | ||||
|     def get_identifier(self) -> int: | ||||
|         """ | ||||
|         Return the Identifier for this Predictors domain. | ||||
|         :return: | ||||
|         """ | ||||
|         return self.__identifier.identifier | ||||
| 
 | ||||
|     def construct_link(self, file_name: str) -> str: | ||||
|         """ | ||||
|         Construct a link by inserting the file_name into the known link pattern | ||||
|         :param file_name: | ||||
|         :return: | ||||
|         """ | ||||
|         raise NotImplementedError | ||||
| 
 | ||||
|     def scrape_tags(self, url: str, headers: dict) -> list: | ||||
|         """ | ||||
|         Scrape the tags from the given url for all tags associated with the work. | ||||
|         :param url: | ||||
|         :param headers: | ||||
|         :return: | ||||
|         """ | ||||
|         raise NotImplementedError | ||||
| 
 | ||||
| 
 | ||||
| class LinkGenerator: | ||||
|     """ | ||||
|     Predict and generate valid links to the file | ||||
|     by matching the given file names against known patterns of the origin domain. | ||||
|     """ | ||||
| 
 | ||||
|     __instance = None  # Singleton holder | ||||
| 
 | ||||
|     def __init__(self): | ||||
|         self.__link_generators = [] | ||||
|         import ArtNet.web.domains  # implements return_all_domains() which returns instances of all domains | ||||
|         # return_all_domains() is to return a list of all DomainLinkGenerator instances that are to be used | ||||
| 
 | ||||
|         for p in ArtNet.web.domains.return_all_domains(): | ||||
|             self.register_domain_predictor(p) | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def get_instance() -> "LinkGenerator": | ||||
|         """ | ||||
|         Gets the current instance | ||||
|         :return: | ||||
|         """ | ||||
|         if LinkGenerator.__instance is None: | ||||
|             LinkGenerator.__instance = LinkGenerator() | ||||
|         return LinkGenerator.__instance | ||||
| 
 | ||||
|     def register_domain_predictor(self, predictor: DomainLinkGenerator): | ||||
|         """ | ||||
|         Register another DomainValidator to be used by this LinkPredictor | ||||
|         :param predictor: | ||||
|         :param domain:  int identifier for the domain | ||||
|         :return: | ||||
|         """ | ||||
|         if predictor not in self.__link_generators: | ||||
|             self.__link_generators.append(predictor) | ||||
| 
 | ||||
|     def predict_domain(self, file_name: str) -> int: | ||||
|         """ | ||||
|         Predict the possible domains of the given file by guessing via the filename | ||||
|         :param file_name: | ||||
|         :return: | ||||
|         """ | ||||
|         for g in self.__link_generators: | ||||
|             try: | ||||
|                 if g.match_file_name(file_name):  # TODO stop accepting first match | ||||
|                     return g.get_identifier() | ||||
|             except NotImplementedError: | ||||
|                 pass | ||||
|         return DOMAIN_UNKNOWN | ||||
| 
 | ||||
|     def construct_link(self, file_name: str, domain: int) -> str: | ||||
|         """ | ||||
|         Construct a valid link to access the web page where the file name (hopefully) originated from. | ||||
|         :param file_name: | ||||
|         :param domain: | ||||
|         :return: | ||||
|         """ | ||||
|         for g in self.__link_generators: | ||||
|             if g.get_identifier() == domain:  # TODO stop accepting first match | ||||
|                 try: | ||||
|                     return g.construct_link(file_name) | ||||
|                 except NotImplementedError: | ||||
|                     return None | ||||
| 
 | ||||
|         return None | ||||
| 
 | ||||
|     def scrape_tags(self, url: str, domain: int) -> dict: | ||||
|         """ | ||||
|         Scrapes the tags from the given url | ||||
|         :param url: | ||||
|         :param domain: | ||||
|         :return: | ||||
|         """ | ||||
|         headers = { | ||||
|             "User-Agent": "Mozilla/5.0 (Windows NT 10.0; rv:76.0) Gecko/20100101 Firefox/76.0" | ||||
|         } | ||||
|         url_domain = urlparse(url).netloc | ||||
|         for g in self.__link_generators: | ||||
|             if g.get_identifier() == domain or g.get_domain_name() == url_domain: | ||||
|                 try: | ||||
|                     return g.scrape_tags(url, headers) | ||||
|                 except NotImplementedError: | ||||
|                     pass | ||||
|         return None | ||||
| @ -0,0 +1,90 @@ | ||||
| # ArtNet | ||||
| 
 | ||||
| ArtNet is a database schema to tag and organize images, videos and  | ||||
| other media files for easy search & traceability. | ||||
| Create your very own reference database! | ||||
| 
 | ||||
| ## Dependencies | ||||
| The database schema has been developed with [PostgreSQL 13](https://www.postgresql.org/). | ||||
| 
 | ||||
| The GUI runs with Python 3.9 as well as: | ||||
|  * PyQT5 | ||||
|  * PyYAML | ||||
|  * other dependencies listed in requirements.txt | ||||
| 
 | ||||
| ## Features | ||||
| 
 | ||||
| ### Editor GUI | ||||
| The GUI allows easy editing, creation and deletion of tags, art entries, | ||||
| presence entries and artists while viewing the file in question. | ||||
| 
 | ||||
| The GUI can connect to different databases via  | ||||
| "ArtNet" > "Change DB Connection" . | ||||
| The login credentials are saved in the directory `.artnet`  | ||||
| located in the current working directory. | ||||
| 
 | ||||
| **Important:** Configuration Encryption is experimental and **unsafe**!  | ||||
| 
 | ||||
| While the GUI makes an attempt to encrypt the configuration | ||||
| file, the password for encryption is, as of writing, inside the source code. | ||||
| The login data should therefore to be treated as plain text and | ||||
| additionally secured in better ways such as proper file permissions. | ||||
| 
 | ||||
| ### Tagging | ||||
| **You can:** | ||||
|  * create, edit & delete your own tags | ||||
|  * imply tags with other tags (e.g. *banana* implies *fruit*) | ||||
|  * create aliases for different tags that mean the same (e.g. *posing* being an alias to *pose*) | ||||
|  * categorize your tags | ||||
|  * create modules to import tagging from known sources | ||||
| 
 | ||||
| ## Concepts & Names | ||||
| 
 | ||||
| ### ArtNet Root | ||||
| The root folder in which your references are stored.  | ||||
| Ultimately the structure is up to you but it is assumed for  | ||||
| new entries to be somewhat like this: | ||||
| ``` | ||||
| ArtNetRoot/ | ||||
|     presence_name_A/ | ||||
|         image_A.jpg | ||||
|         vid_B.mp4 | ||||
|     presence_name_B/ | ||||
|         image_D.jpg | ||||
|     presence_name_C/ | ||||
|         image_C.jpg | ||||
| ``` | ||||
| A structure like this will allow to the GUI to show a correct guess | ||||
| for the presence name when encountering an unknown art entry. | ||||
| 
 | ||||
| ### Presences & Authors | ||||
| In ArtNet you can create a presence for every account an artist | ||||
| has on different or the same website. These presences are connected to | ||||
| an Artist entry and therefore allow the connection of many accounts to the same artist. | ||||
| 
 | ||||
| A presence consists of the presence name and the domain it is used on as well as an  | ||||
| optional direct link to the account. | ||||
| 
 | ||||
| Multiple presences can be associated with any given art allowing for correct | ||||
| attribution for collaborations and similar. | ||||
| 
 | ||||
| -- TODO add artist_presence_relation_example_diagram here -- | ||||
| 
 | ||||
| ### Art | ||||
| Basically the files to be organized and tagged. | ||||
| Each art piece is a file associated with many tags, a source link and | ||||
| one or more presences. | ||||
| 
 | ||||
| ### Tag | ||||
| A tag is keyword that is later to be associated with Art. | ||||
| 
 | ||||
| #### Aliases | ||||
| Each tag can have aliases which are to be considered equal to | ||||
| the current tag and therefore also associated with the art. | ||||
| This relation is bidirectional. | ||||
| 
 | ||||
| #### Implications | ||||
| Each tag can also imply other tags, meaning when | ||||
| the current tag is associated with an art piece the implied tags | ||||
| are to be considered associated too but not vice versa. | ||||
| This relation is unidirectional. | ||||
| @ -0,0 +1,10 @@ | ||||
| from ArtNet.artnet_manager import ArtNetManager | ||||
| 
 | ||||
| # TODO fix DB bug | ||||
| # TODO 1. Open known image | ||||
| # TODO 2. edit a tag on the current image & save the edit | ||||
| # TODO 3. attempt to save the current image | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     am = ArtNetManager() | ||||
|     am.run() | ||||
					Loading…
					
					
				
		Reference in New Issue