From 030c817323446c46325a04530dd40e8aa2c6dc20 Mon Sep 17 00:00:00 2001 From: TechCrafterGaming Date: Mon, 29 Apr 2024 23:57:35 +0200 Subject: [PATCH 01/11] added action to close a library --- tagstudio/src/qt/ts_qt.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/tagstudio/src/qt/ts_qt.py b/tagstudio/src/qt/ts_qt.py index 0127f35..a914363 100644 --- a/tagstudio/src/qt/ts_qt.py +++ b/tagstudio/src/qt/ts_qt.py @@ -249,7 +249,9 @@ class QtDriver(QObject): file_menu.addSeparator() - file_menu.addAction(QAction('&Close Library', menu_bar)) + close_library_action = QAction('&Close Library', menu_bar) + close_library_action.triggered.connect(lambda: self.close_library()) + file_menu.addAction(close_library_action) # Edit Menu ============================================================ new_tag_action = QAction('New &Tag', menu_bar) @@ -422,6 +424,28 @@ class QtDriver(QObject): end_time = time.time() self.main_window.statusbar.showMessage(f'Library Saved! ({format_timespan(end_time - start_time)})') + def close_library(self): + logging.info(f'Closing & Saving Library...') + self.main_window.statusbar.showMessage(f'Closed & Saving Library...') + start_time = time.time() + self.lib.save_library_to_disk() + end_time = time.time() + + # FIXME: idk if all this is necessary to reset the window properly + self.lib.clear_internal_vars() + + title_text = f'{self.base_title}' + self.main_window.setWindowTitle(title_text) + + self.nav_frames: list[NavigationState] = [] + self.cur_frame_idx: int = -1 + self.cur_query: str = '' + self.selected.clear() + self.preview_panel.update_widgets() + self.filter_items() + + self.main_window.statusbar.showMessage(f'Library Saved and Closed! ({format_timespan(end_time - start_time)})') + def backup_library(self): logging.info(f'Backing Up Library...') self.main_window.statusbar.showMessage(f'Saving Library...') From 16a9d32ef30eff84f05ca562ec12568a6ec4198c Mon Sep 17 00:00:00 2001 From: TechCrafterGaming Date: Tue, 30 Apr 2024 08:02:46 +0200 Subject: [PATCH 02/11] added `last_library` value to be set properly --- tagstudio/src/qt/ts_qt.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tagstudio/src/qt/ts_qt.py b/tagstudio/src/qt/ts_qt.py index a914363..28eda23 100644 --- a/tagstudio/src/qt/ts_qt.py +++ b/tagstudio/src/qt/ts_qt.py @@ -429,7 +429,8 @@ class QtDriver(QObject): self.main_window.statusbar.showMessage(f'Closed & Saving Library...') start_time = time.time() self.lib.save_library_to_disk() - end_time = time.time() + self.settings.setValue("last_library", self.lib.library_dir) + self.settings.sync() # FIXME: idk if all this is necessary to reset the window properly self.lib.clear_internal_vars() @@ -444,6 +445,7 @@ class QtDriver(QObject): self.preview_panel.update_widgets() self.filter_items() + end_time = time.time() self.main_window.statusbar.showMessage(f'Library Saved and Closed! ({format_timespan(end_time - start_time)})') def backup_library(self): From 7cdb26e8220ff8c3e169f3fe95c6529b7d8f608b Mon Sep 17 00:00:00 2001 From: TechCrafterGaming Date: Tue, 30 Apr 2024 08:04:13 +0200 Subject: [PATCH 03/11] added if statement --- tagstudio/src/qt/ts_qt.py | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/tagstudio/src/qt/ts_qt.py b/tagstudio/src/qt/ts_qt.py index 28eda23..71e479b 100644 --- a/tagstudio/src/qt/ts_qt.py +++ b/tagstudio/src/qt/ts_qt.py @@ -425,28 +425,29 @@ class QtDriver(QObject): self.main_window.statusbar.showMessage(f'Library Saved! ({format_timespan(end_time - start_time)})') def close_library(self): - logging.info(f'Closing & Saving Library...') - self.main_window.statusbar.showMessage(f'Closed & Saving Library...') - start_time = time.time() - self.lib.save_library_to_disk() - self.settings.setValue("last_library", self.lib.library_dir) - self.settings.sync() + if self.lib.library_dir: + logging.info(f'Closing & Saving Library...') + self.main_window.statusbar.showMessage(f'Closed & Saving Library...') + start_time = time.time() + self.lib.save_library_to_disk() + self.settings.setValue("last_library", self.lib.library_dir) + self.settings.sync() - # FIXME: idk if all this is necessary to reset the window properly - self.lib.clear_internal_vars() - - title_text = f'{self.base_title}' - self.main_window.setWindowTitle(title_text) + # FIXME: idk if all this is necessary to reset the window properly + self.lib.clear_internal_vars() + + title_text = f'{self.base_title}' + self.main_window.setWindowTitle(title_text) - self.nav_frames: list[NavigationState] = [] - self.cur_frame_idx: int = -1 - self.cur_query: str = '' - self.selected.clear() - self.preview_panel.update_widgets() - self.filter_items() + self.nav_frames: list[NavigationState] = [] + self.cur_frame_idx: int = -1 + self.cur_query: str = '' + self.selected.clear() + self.preview_panel.update_widgets() + self.filter_items() - end_time = time.time() - self.main_window.statusbar.showMessage(f'Library Saved and Closed! ({format_timespan(end_time - start_time)})') + end_time = time.time() + self.main_window.statusbar.showMessage(f'Library Saved and Closed! ({format_timespan(end_time - start_time)})') def backup_library(self): logging.info(f'Backing Up Library...') From a669fd67c361e8382659e56acabb3f9997561c55 Mon Sep 17 00:00:00 2001 From: TechCrafterGaming Date: Tue, 30 Apr 2024 08:07:10 +0200 Subject: [PATCH 04/11] small adjustments and comments --- tagstudio/src/qt/ts_qt.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tagstudio/src/qt/ts_qt.py b/tagstudio/src/qt/ts_qt.py index 71e479b..24f83bd 100644 --- a/tagstudio/src/qt/ts_qt.py +++ b/tagstudio/src/qt/ts_qt.py @@ -426,6 +426,7 @@ class QtDriver(QObject): def close_library(self): if self.lib.library_dir: + # TODO: it is kinda the same code from "save_library"... logging.info(f'Closing & Saving Library...') self.main_window.statusbar.showMessage(f'Closed & Saving Library...') start_time = time.time() @@ -433,9 +434,7 @@ class QtDriver(QObject): self.settings.setValue("last_library", self.lib.library_dir) self.settings.sync() - # FIXME: idk if all this is necessary to reset the window properly self.lib.clear_internal_vars() - title_text = f'{self.base_title}' self.main_window.setWindowTitle(title_text) From d478116094165603fb8e8d5fc6ee2bf5f91b70c4 Mon Sep 17 00:00:00 2001 From: yedpodtrzitko Date: Wed, 1 May 2024 00:04:37 +0800 Subject: [PATCH 05/11] show all tags in tag panel by default --- tagstudio/src/core/library.py | 3 +- tagstudio/src/qt/modals/tag_search.py | 92 +++++++++++++-------------- 2 files changed, 46 insertions(+), 49 deletions(-) diff --git a/tagstudio/src/core/library.py b/tagstudio/src/core/library.py index 375252e..d463dc5 100644 --- a/tagstudio/src/core/library.py +++ b/tagstudio/src/core/library.py @@ -1577,7 +1577,8 @@ class Library: # NOTE: I'd expect a blank query to return all with the other implementation, but # it misses stuff like Archive (id 0) so here's this as a catch-all. - if query == '': + query = query.strip() + if not query: all: list[int] = [] for tag in self.tags: if ignore_builtin and tag.id >= 1000: diff --git a/tagstudio/src/qt/modals/tag_search.py b/tagstudio/src/qt/modals/tag_search.py index 5942824..7f79f8f 100644 --- a/tagstudio/src/qt/modals/tag_search.py +++ b/tagstudio/src/qt/modals/tag_search.py @@ -27,7 +27,7 @@ class TagSearchPanel(PanelWidget): super().__init__() self.lib: Library = library # self.callback = callback - self.first_tag_id = -1 + self.first_tag_id = None self.tag_limit = 30 # self.selected_tag: int = 0 self.setMinimumSize(300, 400) @@ -68,6 +68,7 @@ class TagSearchPanel(PanelWidget): self.root_layout.addWidget(self.search_field) self.root_layout.addWidget(self.scroll_area) + self.update_tags('') # def reset(self): # self.search_field.setText('') @@ -75,7 +76,7 @@ class TagSearchPanel(PanelWidget): # self.search_field.setFocus() def on_return(self, text:str): - if text and self.first_tag_id >= 0: + if text and self.first_tag_id is not None: # callback(self.first_tag_id) self.tag_chosen.emit(self.first_tag_id) self.search_field.setText('') @@ -87,57 +88,52 @@ class TagSearchPanel(PanelWidget): def update_tags(self, query:str): # for c in self.scroll_layout.children(): # c.widget().deleteLater() - while self.scroll_layout.itemAt(0): + while self.scroll_layout.count(): # logging.info(f"I'm deleting { self.scroll_layout.itemAt(0).widget()}") self.scroll_layout.takeAt(0).widget().deleteLater() - - if query: - first_id_set = False - for tag_id in self.lib.search_tags(query, include_cluster=True)[:self.tag_limit-1]: - if not first_id_set: - self.first_tag_id = tag_id - first_id_set = True - c = QWidget() - l = QHBoxLayout(c) - l.setContentsMargins(0,0,0,0) - l.setSpacing(3) - tw = TagWidget(self.lib, self.lib.get_tag(tag_id), False, False) - ab = QPushButton() - ab.setMinimumSize(23, 23) - ab.setMaximumSize(23, 23) - ab.setText('+') - ab.setStyleSheet( - f'QPushButton{{' - f'background: {get_tag_color(ColorType.PRIMARY, self.lib.get_tag(tag_id).color)};' - # f'background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 {get_tag_color(ColorType.PRIMARY, tag.color)}, stop:1.0 {get_tag_color(ColorType.BORDER, tag.color)});' - # f"border-color:{get_tag_color(ColorType.PRIMARY, tag.color)};" - f"color: {get_tag_color(ColorType.TEXT, self.lib.get_tag(tag_id).color)};" - f'font-weight: 600;' - f"border-color:{get_tag_color(ColorType.BORDER, self.lib.get_tag(tag_id).color)};" - f'border-radius: 6px;' - f'border-style:solid;' - f'border-width: {math.ceil(1*self.devicePixelRatio())}px;' - # f'padding-top: 1.5px;' - # f'padding-right: 4px;' - f'padding-bottom: 5px;' - # f'padding-left: 4px;' - f'font-size: 20px;' - f'}}' - f'QPushButton::hover' - f'{{' - f"border-color:{get_tag_color(ColorType.LIGHT_ACCENT, self.lib.get_tag(tag_id).color)};" - f"color: {get_tag_color(ColorType.DARK_ACCENT, self.lib.get_tag(tag_id).color)};" - f'background: {get_tag_color(ColorType.LIGHT_ACCENT, self.lib.get_tag(tag_id).color)};' - f'}}') + found_tags = self.lib.search_tags(query, include_cluster=True)[:self.tag_limit - 1] + self.first_tag_id = found_tags[0] if found_tags else None - ab.clicked.connect(lambda checked=False, x=tag_id: self.tag_chosen.emit(x)) + for tag_id in found_tags: + c = QWidget() + l = QHBoxLayout(c) + l.setContentsMargins(0, 0, 0, 0) + l.setSpacing(3) + tw = TagWidget(self.lib, self.lib.get_tag(tag_id), False, False) + ab = QPushButton() + ab.setMinimumSize(23, 23) + ab.setMaximumSize(23, 23) + ab.setText('+') + ab.setStyleSheet( + f'QPushButton{{' + f'background: {get_tag_color(ColorType.PRIMARY, self.lib.get_tag(tag_id).color)};' + # f'background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 {get_tag_color(ColorType.PRIMARY, tag.color)}, stop:1.0 {get_tag_color(ColorType.BORDER, tag.color)});' + # f"border-color:{get_tag_color(ColorType.PRIMARY, tag.color)};" + f"color: {get_tag_color(ColorType.TEXT, self.lib.get_tag(tag_id).color)};" + f'font-weight: 600;' + f"border-color:{get_tag_color(ColorType.BORDER, self.lib.get_tag(tag_id).color)};" + f'border-radius: 6px;' + f'border-style:solid;' + f'border-width: {math.ceil(1*self.devicePixelRatio())}px;' + # f'padding-top: 1.5px;' + # f'padding-right: 4px;' + f'padding-bottom: 5px;' + # f'padding-left: 4px;' + f'font-size: 20px;' + f'}}' + f'QPushButton::hover' + f'{{' + f"border-color:{get_tag_color(ColorType.LIGHT_ACCENT, self.lib.get_tag(tag_id).color)};" + f"color: {get_tag_color(ColorType.DARK_ACCENT, self.lib.get_tag(tag_id).color)};" + f'background: {get_tag_color(ColorType.LIGHT_ACCENT, self.lib.get_tag(tag_id).color)};' + f'}}') - l.addWidget(tw) - l.addWidget(ab) - self.scroll_layout.addWidget(c) - else: - self.first_tag_id = -1 + ab.clicked.connect(lambda checked=False, x=tag_id: self.tag_chosen.emit(x)) + + l.addWidget(tw) + l.addWidget(ab) + self.scroll_layout.addWidget(c) self.search_field.setFocus() From f9fc28d5ec5b911ac008abb2d7ef94f9c8294262 Mon Sep 17 00:00:00 2001 From: yedpodtrzitko Date: Wed, 1 May 2024 15:46:08 +0800 Subject: [PATCH 06/11] fix file opening on posix --- tagstudio/src/cli/ts_cli.py | 20 +++++----- tagstudio/src/qt/helpers/file_opener.py | 51 ++++++++++++++++--------- 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/tagstudio/src/cli/ts_cli.py b/tagstudio/src/cli/ts_cli.py index 30e14ae..a26c571 100644 --- a/tagstudio/src/cli/ts_cli.py +++ b/tagstudio/src/cli/ts_cli.py @@ -23,6 +23,7 @@ from src.core.ts_core import * from src.core.utils.web import * from src.core.utils.fs import * from src.core.library import * +from src.qt.helpers.file_opener import file_open WHITE_FG = '\033[37m' WHITE_BG = '\033[47m' @@ -352,8 +353,8 @@ class CliDriver: if not os.path.isfile(external_preview_path): temp = self.external_preview_default temp.save(external_preview_path) - if os.path.isfile(external_preview_path): - os.startfile(external_preview_path) + + file_open(external_preview_path) def set_external_preview_default(self) -> None: """Sets the external preview to its default image.""" @@ -1706,8 +1707,7 @@ class CliDriver: args = ['explorer', '/select,', filename] subprocess.call(args) else: - if os.path.isfile(filename): - os.startfile(filename) + file_open(filename) # refresh=False # self.scr_browse_entries_gallery(index) # Add Field ============================================================ @@ -2152,9 +2152,8 @@ class CliDriver: # Open ============================================================= elif (com[0].lower() == 'open' or com[0].lower() == 'o'): for match in self.lib.missing_matches[filename]: - fn = f'{os.path.normpath(self.lib.library_dir + "/" + match + "/" + entry.filename)}' - if os.path.isfile(fn): - os.startfile(fn) + fn = os.path.normpath(self.lib.library_dir + "/" + match + "/" + entry.filename) + file_open(fn) refresh = False # clear() # return self.scr_choose_missing_match(index, clear_scr=False) @@ -2276,8 +2275,8 @@ class CliDriver: # fn = f'{os.path.normpath(self.lib.library_dir + "/" + match + "/" + entry_1.filename)}' # if os.path.isfile(fn): # os.startfile(fn) - os.startfile(dupe[0]) - os.startfile(dupe[1]) + file_open(dupe[0]) + file_open(dupe[1]) # clear() # return self.scr_resolve_dupe_files(index, clear_scr=False) # Mirror Entries =================================================== @@ -2385,8 +2384,7 @@ class CliDriver: # Open with Default Application ======================================== if (com[0].lower() == 'open' or com[0].lower() == 'o'): - if os.path.isfile(filename): - os.startfile(filename) + file_open(filename) # self.scr_edit_entry_tag_box(entry_index, field_index) # return # Close View =========================================================== diff --git a/tagstudio/src/qt/helpers/file_opener.py b/tagstudio/src/qt/helpers/file_opener.py index e6703f1..747144c 100644 --- a/tagstudio/src/qt/helpers/file_opener.py +++ b/tagstudio/src/qt/helpers/file_opener.py @@ -5,6 +5,8 @@ import logging import os import subprocess +import sys +from os.path import isfile from PySide6.QtWidgets import QLabel @@ -15,7 +17,20 @@ INFO = f'[INFO]' logging.basicConfig(format="%(message)s", level=logging.INFO) -class FileOpenerHelper(): +def file_open(filepath: str, check_first: bool = True): + if check_first and not os.path.isfile(filepath): + logging.error(f'File not found: {filepath}') + return False + + if os.name == 'nt': + os.startfile(filepath) + elif sys.platform == 'darwin': + subprocess.Popen(['open', filepath]) + else: + subprocess.call(["xdg-open", filepath]) + + +class FileOpenerHelper: def __init__(self, filepath:str): self.filepath = filepath @@ -23,26 +38,26 @@ class FileOpenerHelper(): self.filepath = filepath def open_file(self): - if os.path.exists(self.filepath): - os.startfile(self.filepath) - logging.info(f'Opening file: {self.filepath}') - else: - logging.error(f'File not found: {self.filepath}') + logging.info(f'Opening file: {self.filepath}') + file_open(self.filepath) def open_explorer(self): - if os.path.exists(self.filepath): - logging.info(f'Opening file: {self.filepath}') - if os.name == 'nt': # Windows - command = f'explorer /select,"{self.filepath}"' - subprocess.run(command, shell=True) - else: # macOS and Linux - command = f'nautilus --select "{self.filepath}"' # Adjust for your Linux file manager if different - if subprocess.run(command, shell=True).returncode == 0: - file_loc = os.path.dirname(self.filepath) - file_loc = os.path.normpath(file_loc) - os.startfile(file_loc) - else: + if not os.path.exists(self.filepath): logging.error(f'File not found: {self.filepath}') + return + + logging.info(f'Opening file: {self.filepath}') + if os.name == 'nt': # Windows + command = f'explorer /select,"{self.filepath}"' + subprocess.run(command, shell=True) + elif sys.platform == 'darwin': + subprocess.Popen(['open', '-R', self.filepath]) + else: # macOS and Linux + command = f'nautilus --select "{self.filepath}"' # Adjust for your Linux file manager if different + if subprocess.run(command, shell=True).returncode == 0: + file_loc = os.path.dirname(self.filepath) + file_loc = os.path.normpath(file_loc) + os.startfile(file_loc) class FileOpenerLabel(QLabel): From f40be005f89959f53ea5a379ff757ffc7ff2559a Mon Sep 17 00:00:00 2001 From: yedpodtrzitko Date: Wed, 1 May 2024 15:46:08 +0800 Subject: [PATCH 07/11] fix file opening on posix --- tagstudio/src/cli/ts_cli.py | 20 +++++----- tagstudio/src/qt/helpers/file_opener.py | 50 ++++++++++++++++--------- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/tagstudio/src/cli/ts_cli.py b/tagstudio/src/cli/ts_cli.py index 30e14ae..a26c571 100644 --- a/tagstudio/src/cli/ts_cli.py +++ b/tagstudio/src/cli/ts_cli.py @@ -23,6 +23,7 @@ from src.core.ts_core import * from src.core.utils.web import * from src.core.utils.fs import * from src.core.library import * +from src.qt.helpers.file_opener import file_open WHITE_FG = '\033[37m' WHITE_BG = '\033[47m' @@ -352,8 +353,8 @@ class CliDriver: if not os.path.isfile(external_preview_path): temp = self.external_preview_default temp.save(external_preview_path) - if os.path.isfile(external_preview_path): - os.startfile(external_preview_path) + + file_open(external_preview_path) def set_external_preview_default(self) -> None: """Sets the external preview to its default image.""" @@ -1706,8 +1707,7 @@ class CliDriver: args = ['explorer', '/select,', filename] subprocess.call(args) else: - if os.path.isfile(filename): - os.startfile(filename) + file_open(filename) # refresh=False # self.scr_browse_entries_gallery(index) # Add Field ============================================================ @@ -2152,9 +2152,8 @@ class CliDriver: # Open ============================================================= elif (com[0].lower() == 'open' or com[0].lower() == 'o'): for match in self.lib.missing_matches[filename]: - fn = f'{os.path.normpath(self.lib.library_dir + "/" + match + "/" + entry.filename)}' - if os.path.isfile(fn): - os.startfile(fn) + fn = os.path.normpath(self.lib.library_dir + "/" + match + "/" + entry.filename) + file_open(fn) refresh = False # clear() # return self.scr_choose_missing_match(index, clear_scr=False) @@ -2276,8 +2275,8 @@ class CliDriver: # fn = f'{os.path.normpath(self.lib.library_dir + "/" + match + "/" + entry_1.filename)}' # if os.path.isfile(fn): # os.startfile(fn) - os.startfile(dupe[0]) - os.startfile(dupe[1]) + file_open(dupe[0]) + file_open(dupe[1]) # clear() # return self.scr_resolve_dupe_files(index, clear_scr=False) # Mirror Entries =================================================== @@ -2385,8 +2384,7 @@ class CliDriver: # Open with Default Application ======================================== if (com[0].lower() == 'open' or com[0].lower() == 'o'): - if os.path.isfile(filename): - os.startfile(filename) + file_open(filename) # self.scr_edit_entry_tag_box(entry_index, field_index) # return # Close View =========================================================== diff --git a/tagstudio/src/qt/helpers/file_opener.py b/tagstudio/src/qt/helpers/file_opener.py index e6703f1..e91a124 100644 --- a/tagstudio/src/qt/helpers/file_opener.py +++ b/tagstudio/src/qt/helpers/file_opener.py @@ -5,6 +5,7 @@ import logging import os import subprocess +import sys from PySide6.QtWidgets import QLabel @@ -15,7 +16,20 @@ INFO = f'[INFO]' logging.basicConfig(format="%(message)s", level=logging.INFO) -class FileOpenerHelper(): +def file_open(filepath: str, check_first: bool = True): + if check_first and not os.path.isfile(filepath): + logging.error(f'File not found: {filepath}') + return False + + if os.name == 'nt': + os.startfile(filepath) + elif sys.platform == 'darwin': + subprocess.Popen(['open', filepath]) + else: + subprocess.call(["xdg-open", filepath]) + + +class FileOpenerHelper: def __init__(self, filepath:str): self.filepath = filepath @@ -23,26 +37,26 @@ class FileOpenerHelper(): self.filepath = filepath def open_file(self): - if os.path.exists(self.filepath): - os.startfile(self.filepath) - logging.info(f'Opening file: {self.filepath}') - else: - logging.error(f'File not found: {self.filepath}') + logging.info(f'Opening file: {self.filepath}') + file_open(self.filepath) def open_explorer(self): - if os.path.exists(self.filepath): - logging.info(f'Opening file: {self.filepath}') - if os.name == 'nt': # Windows - command = f'explorer /select,"{self.filepath}"' - subprocess.run(command, shell=True) - else: # macOS and Linux - command = f'nautilus --select "{self.filepath}"' # Adjust for your Linux file manager if different - if subprocess.run(command, shell=True).returncode == 0: - file_loc = os.path.dirname(self.filepath) - file_loc = os.path.normpath(file_loc) - os.startfile(file_loc) - else: + if not os.path.exists(self.filepath): logging.error(f'File not found: {self.filepath}') + return + + logging.info(f'Opening file: {self.filepath}') + if os.name == 'nt': # Windows + command = f'explorer /select,"{self.filepath}"' + subprocess.run(command, shell=True) + elif sys.platform == 'darwin': + subprocess.Popen(['open', '-R', self.filepath]) + else: # Linux + command = f'nautilus --select "{self.filepath}"' # Adjust for your Linux file manager if different + if subprocess.run(command, shell=True).returncode == 0: + file_loc = os.path.dirname(self.filepath) + file_loc = os.path.normpath(file_loc) + os.startfile(file_loc) class FileOpenerLabel(QLabel): From c58590a22153f6ebab8fa21ba5eca3568f6ff77e Mon Sep 17 00:00:00 2001 From: Xarvex Date: Wed, 1 May 2024 03:26:36 -0500 Subject: [PATCH 08/11] Make use of open_file from #84 Refactor done in #80 essentially reverted changes Re-fixes #81, also re-fixes windows opening behind TagStudio --- tagstudio/src/cli/ts_cli.py | 5 +- tagstudio/src/qt/helpers/__init__.py | 5 +- tagstudio/src/qt/helpers/file_opener.py | 74 +++++++++++++++---------- tagstudio/src/qt/helpers/open_file.py | 49 ---------------- 4 files changed, 48 insertions(+), 85 deletions(-) delete mode 100644 tagstudio/src/qt/helpers/open_file.py diff --git a/tagstudio/src/cli/ts_cli.py b/tagstudio/src/cli/ts_cli.py index a26c571..7ce1199 100644 --- a/tagstudio/src/cli/ts_cli.py +++ b/tagstudio/src/cli/ts_cli.py @@ -23,7 +23,7 @@ from src.core.ts_core import * from src.core.utils.web import * from src.core.utils.fs import * from src.core.library import * -from src.qt.helpers.file_opener import file_open +from src.qt.helpers.file_opener import open_file WHITE_FG = '\033[37m' WHITE_BG = '\033[47m' @@ -2273,8 +2273,7 @@ class CliDriver: elif (com[0].lower() == 'open' or com[0].lower() == 'o'): # for match in self.lib.missing_matches[filename]: # fn = f'{os.path.normpath(self.lib.library_dir + "/" + match + "/" + entry_1.filename)}' - # if os.path.isfile(fn): - # os.startfile(fn) + # open_file(fn) file_open(dupe[0]) file_open(dupe[1]) # clear() diff --git a/tagstudio/src/qt/helpers/__init__.py b/tagstudio/src/qt/helpers/__init__.py index f66a647..a79b66a 100644 --- a/tagstudio/src/qt/helpers/__init__.py +++ b/tagstudio/src/qt/helpers/__init__.py @@ -1,4 +1,3 @@ -from .open_file import open_file -from .file_opener import FileOpenerHelper, FileOpenerLabel +from .file_opener import open_file, FileOpenerHelper, FileOpenerLabel from .function_iterator import FunctionIterator -from .custom_runnable import CustomRunnable \ No newline at end of file +from .custom_runnable import CustomRunnable diff --git a/tagstudio/src/qt/helpers/file_opener.py b/tagstudio/src/qt/helpers/file_opener.py index 747144c..25f5d1d 100644 --- a/tagstudio/src/qt/helpers/file_opener.py +++ b/tagstudio/src/qt/helpers/file_opener.py @@ -5,8 +5,9 @@ import logging import os import subprocess +import shutil import sys -from os.path import isfile +import traceback from PySide6.QtWidgets import QLabel @@ -17,17 +18,46 @@ INFO = f'[INFO]' logging.basicConfig(format="%(message)s", level=logging.INFO) -def file_open(filepath: str, check_first: bool = True): - if check_first and not os.path.isfile(filepath): - logging.error(f'File not found: {filepath}') - return False - - if os.name == 'nt': - os.startfile(filepath) - elif sys.platform == 'darwin': - subprocess.Popen(['open', filepath]) - else: - subprocess.call(["xdg-open", filepath]) +def open_file(path: str, file_manager: bool = False): + logging.info(f'Opening file: {path}') + if not os.path.exists(path): + logging.error(f'File not found: {path}') + return + try: + if sys.platform == "win32": + normpath = os.path.normpath(path) + if file_manager: + command_name = "explorer" + command_args = [f"/select,{normpath}"] + else: + command_name = "start" + # first parameter is for title, NOT filepath + command_args = ["", normpath] + subprocess.Popen([command_name] + command_args, shell=True, close_fds=True, creationflags=subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.CREATE_BREAKAWAY_FROM_JOB) + else: + if sys.platform == "darwin": + command_name = "open" + command_args = [path] + if file_manager: + # will reveal in Finder + command_args.append("-R") + else: + if file_manager: + command_name = "dbus-send" + # might not be guaranteed to launch default? + command_args = ["--session", "--dest=org.freedesktop.FileManager1", "--type=method_call", + "/org/freedesktop/FileManager1", "org.freedesktop.FileManager1.ShowItems", + f"array:string:file://{path}", "string:"] + else: + command_name = "xdg-open" + command_args = [path] + command = shutil.which(command_name) + if command is not None: + subprocess.Popen([command] + command_args, close_fds=True) + else: + logging.info(f"Could not find {command_name} on system PATH") + except: + traceback.print_exc() class FileOpenerHelper: @@ -38,26 +68,10 @@ class FileOpenerHelper: self.filepath = filepath def open_file(self): - logging.info(f'Opening file: {self.filepath}') - file_open(self.filepath) + open_file(self.filepath) def open_explorer(self): - if not os.path.exists(self.filepath): - logging.error(f'File not found: {self.filepath}') - return - - logging.info(f'Opening file: {self.filepath}') - if os.name == 'nt': # Windows - command = f'explorer /select,"{self.filepath}"' - subprocess.run(command, shell=True) - elif sys.platform == 'darwin': - subprocess.Popen(['open', '-R', self.filepath]) - else: # macOS and Linux - command = f'nautilus --select "{self.filepath}"' # Adjust for your Linux file manager if different - if subprocess.run(command, shell=True).returncode == 0: - file_loc = os.path.dirname(self.filepath) - file_loc = os.path.normpath(file_loc) - os.startfile(file_loc) + open_file(self.filepath, True) class FileOpenerLabel(QLabel): diff --git a/tagstudio/src/qt/helpers/open_file.py b/tagstudio/src/qt/helpers/open_file.py deleted file mode 100644 index f2e070e..0000000 --- a/tagstudio/src/qt/helpers/open_file.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (C) 2024 Travis Abendshien (CyanVoxel). -# Licensed under the GPL-3.0 License. -# Created for TagStudio: https://github.com/CyanVoxel/TagStudio - - -import logging -import os -import sys -import traceback -import shutil -import subprocess - - -def open_file(path: str, file_manager: bool = False): - try: - if sys.platform == "win32": - normpath = os.path.normpath(path) - if file_manager: - command_name = "explorer" - command_args = [f"/select,{normpath}"] - else: - command_name = "start" - # first parameter is for title, NOT filepath - command_args = ["", normpath] - subprocess.Popen([command_name] + command_args, shell=True, close_fds=True, creationflags=subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.CREATE_BREAKAWAY_FROM_JOB) - else: - if sys.platform == "darwin": - command_name = "open" - command_args = [path] - if file_manager: - # will reveal in Finder - command_args.append("-R") - else: - if file_manager: - command_name = "dbus-send" - # might not be guaranteed to launch default? - command_args = ["--session", "--dest=org.freedesktop.FileManager1", "--type=method_call", - "/org/freedesktop/FileManager1", "org.freedesktop.FileManager1.ShowItems", - f"array:string:file://{path}", "string:"] - else: - command_name = "xdg-open" - command_args = [path] - command = shutil.which(command_name) - if command is not None: - subprocess.Popen([command] + command_args, close_fds=True) - else: - logging.info(f"Could not find {command_name} on system PATH") - except: - traceback.print_exc() \ No newline at end of file From 81e0e7b072e4c132e3065602a0e02a59ce7ae2ab Mon Sep 17 00:00:00 2001 From: Xarvex Date: Wed, 1 May 2024 03:50:27 -0500 Subject: [PATCH 09/11] Accidental double sys import during merge conflict --- tagstudio/src/qt/helpers/file_opener.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tagstudio/src/qt/helpers/file_opener.py b/tagstudio/src/qt/helpers/file_opener.py index d2e2ffc..25f5d1d 100644 --- a/tagstudio/src/qt/helpers/file_opener.py +++ b/tagstudio/src/qt/helpers/file_opener.py @@ -8,7 +8,6 @@ import subprocess import shutil import sys import traceback -import sys from PySide6.QtWidgets import QLabel From 8604a7f08f3a7f23c6b1c51bc5e5276ba8e8aa61 Mon Sep 17 00:00:00 2001 From: yedpodtrzitko Date: Wed, 1 May 2024 17:15:33 +0800 Subject: [PATCH 10/11] fix image thumbnails rotation --- tagstudio/src/qt/widgets/thumb_renderer.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tagstudio/src/qt/widgets/thumb_renderer.py b/tagstudio/src/qt/widgets/thumb_renderer.py index f13d0a4..211f21e 100644 --- a/tagstudio/src/qt/widgets/thumb_renderer.py +++ b/tagstudio/src/qt/widgets/thumb_renderer.py @@ -10,7 +10,7 @@ import os from pathlib import Path import cv2 -from PIL import Image, ImageChops, UnidentifiedImageError, ImageQt, ImageDraw, ImageFont, ImageEnhance +from PIL import Image, ImageChops, UnidentifiedImageError, ImageQt, ImageDraw, ImageFont, ImageEnhance, ImageOps from PySide6.QtCore import QObject, Signal, QSize from PySide6.QtGui import QPixmap from src.core.ts_core import PLAINTEXT_TYPES, VIDEO_TYPES, IMAGE_TYPES @@ -60,9 +60,6 @@ class ThumbRenderer(QObject): ext_font = ImageFont.truetype(os.path.normpath( f'{Path(__file__).parent.parent.parent.parent}/resources/qt/fonts/Oxanium-Bold.ttf'), math.floor(12*font_pixel_ratio)) - def __init__(self): - QObject.__init__(self) - def render(self, timestamp: float, filepath, base_size: tuple[int, int], pixelRatio: float, isLoading=False): """Renders an entry/element thumbnail for the GUI.""" adj_size: int = 1 @@ -107,6 +104,8 @@ class ThumbRenderer(QObject): if image.mode != 'RGB': image = image.convert(mode='RGB') + image = ImageOps.exif_transpose(image) + # Videos ======================================================= elif extension in VIDEO_TYPES: video = cv2.VideoCapture(filepath) From 3d5ed3a9489d182a1b06f225e0566ac8f9d56f76 Mon Sep 17 00:00:00 2001 From: Travis Abendshien Date: Wed, 1 May 2024 02:58:43 -0700 Subject: [PATCH 11/11] Added EXIF transpose to small thumbnails --- tagstudio/src/qt/widgets/thumb_renderer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tagstudio/src/qt/widgets/thumb_renderer.py b/tagstudio/src/qt/widgets/thumb_renderer.py index 211f21e..be60030 100644 --- a/tagstudio/src/qt/widgets/thumb_renderer.py +++ b/tagstudio/src/qt/widgets/thumb_renderer.py @@ -264,6 +264,8 @@ class ThumbRenderer(QObject): image = new_bg if image.mode != 'RGB': image = image.convert(mode='RGB') + + image = ImageOps.exif_transpose(image) # Videos ======================================================= elif extension in VIDEO_TYPES: