From 2f76622e52f012543f5bf1c37be3748e61efe14e Mon Sep 17 00:00:00 2001 From: Tyler Date: Thu, 13 Aug 2026 14:34:51 -0700 Subject: [PATCH] file association fixes macOS --- changelog.md | 6 ++++++ file_ext_registration.py | 43 ++-------------------------------------- main.py | 25 ++++++++++++++++++++--- src/shared/shareddata.py | 2 +- 4 files changed, 31 insertions(+), 45 deletions(-) diff --git a/changelog.md b/changelog.md index 28ffc70..d18085e 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,9 @@ +# Version 1.6.1 + +- Fixed an issue where file associations appeared to work but would not load the project on macOS +- Fixed an issue where file associations would refuse to assosciate on macOS + + # Version 1.6.0 - This is potentially a save-changing release due to adding more data into the save file, renaming existing data, and changing what part of the code saves data. Please update your project files to ensure compatibility diff --git a/file_ext_registration.py b/file_ext_registration.py index 36085dc..510ddbd 100644 --- a/file_ext_registration.py +++ b/file_ext_registration.py @@ -337,45 +337,6 @@ def _register_macos(ext: str, app_name: str, bundle_id: str) -> Tuple[bool, str] if not app_bundle_path.endswith(".app"): return False, "Could not locate outer .app bundle." - info_plist_path = os.path.join(app_bundle_path, "Contents", "Info.plist") - if not os.path.exists(info_plist_path): - return False, f"Info.plist not found at {info_plist_path}" - - clean_ext = ext.lstrip('.') - uti = f"{bundle_id}.{clean_ext}" - - try: - with open(info_plist_path, "rb") as f: - plist = plistlib.load(f) - - # Tell macOS this app can open files with our UTI - doc_types = plist.get("CFBundleDocumentTypes", []) - if not any(uti in dt.get("LSItemContentTypes", []) for dt in doc_types): - doc_types.append({ - "CFBundleTypeName": f"{app_name} Project File", - "CFBundleTypeRole": "Editor", - "LSHandlerRank": "Owner", - "LSItemContentTypes": [uti], - }) - plist["CFBundleDocumentTypes"] = doc_types - - # Declare the UTI itself (required — without this the type is unknown to LS) - exported_types = plist.get("UTExportedTypeDeclarations", []) - if not any(t.get("UTTypeIdentifier") == uti for t in exported_types): - exported_types.append({ - "UTTypeIdentifier": uti, - "UTTypeDescription": f"{app_name} File", - "UTTypeConformsTo": ["public.data"], - "UTTypeTagSpecification": {"public.filename-extension": [clean_ext]}, - }) - plist["UTExportedTypeDeclarations"] = exported_types - - with open(info_plist_path, "wb") as f: - plistlib.dump(plist, f) - - except Exception as e: - return False, f"Failed to update Info.plist: {str(e)}" - try: lsregister_path = ( "/System/Library/Frameworks/CoreServices.framework/Frameworks/" @@ -385,9 +346,9 @@ def _register_macos(ext: str, app_name: str, bundle_id: str) -> Tuple[bool, str] [lsregister_path, "-f", app_bundle_path], check=True, capture_output=True, ) - return True, f"Registered {ext} with {app_bundle_path} via macOS Launch Services!" + return True, f"Refreshed Launch Services registration for {app_bundle_path}." except subprocess.CalledProcessError as e: stderr = e.stderr.decode(errors="ignore") if e.stderr else str(e) - return False, f"macOS Launch Services registration failed: {stderr}" + return False, f"macOS Launch Services refresh failed: {stderr}" except Exception as e: return False, f"macOS Registration failed: {str(e)}" \ No newline at end of file diff --git a/main.py b/main.py index 0e2905d..76c5442 100644 --- a/main.py +++ b/main.py @@ -28,7 +28,7 @@ from PySide6.QtWidgets import ( QApplication, QWidget, QMessageBox, QVBoxLayout, QHBoxLayout, QTextEdit, QScrollArea, QComboBox, QGridLayout, QSplitter, QDialogButtonBox, QHeaderView, QPushButton, QMainWindow, QLabel, QLineEdit, QGroupBox, QDialog, QMenu, QSpinBox, QTableWidget, QTableWidgetItem ) -from PySide6.QtCore import Signal, Qt, QTimer +from PySide6.QtCore import QEvent, Signal, Qt, QTimer from PySide6.QtGui import QAction, QFontMetrics, QKeySequence, QIcon from PySide6.QtSvgWidgets import QSvgWidget # needed to show svgs when app is not frozen @@ -488,7 +488,26 @@ class GroupAssignmentDialog(QDialog): return mappings + +class CustomApplication(QApplication): + """ + macOS delivers a file-open request (double-clicking a registered + file, or dropping one on the Dock icon — whether the app is already + running or being launched fresh) as an Apple Event, which Qt exposes + as QEvent.Type.FileOpen. Windows/Linux never send this; they pass + the path as a normal argv argument, which startup_args.initial_file + already handles. Without this override, double-clicking a project + file on macOS launches the app but it never learns which file to open. + """ + file_open_requested = Signal(str) + + def event(self, e: QEvent) -> bool: + if e.type() == QEvent.Type.FileOpen: + self.file_open_requested.emit(e.file()) + return True + return super().event(e) + class MainApplication(QMainWindow): """ @@ -1334,7 +1353,6 @@ class MainApplication(QMainWindow): def apply_splitter_ratios(self): """Applies saved ratio positions to main_h_splitter and left_v_splitter.""" if hasattr(self, 'main_h_splitter'): - print("Splitter actual width:", self.main_h_splitter.width()) total_width = self.main_h_splitter.width() if total_width > 0: left_w = int(total_width * self.main_h_ratio) @@ -2648,11 +2666,12 @@ if __name__ == "__main__": # Only run GUI in the main process if current_process().name == 'MainProcess': - app = QApplication(sys.argv) + app = CustomApplication(sys.argv) finish_update_if_needed(PLATFORM_NAME, APP_NAME, cfg_path, startup_args.finish_update) icon_ext = "icns" if PLATFORM_NAME == "darwin" else "ico" app.setWindowIcon(QIcon(resource_path(f"icons/main.{icon_ext}"))) window = MainApplication(file_to_open=startup_args.initial_file) + app.file_open_requested.connect(window.project_manager.load_project) window.setWindowIcon(QIcon(resource_path(f"icons/main.{icon_ext}"))) window.show() sys.exit(app.exec()) diff --git a/src/shared/shareddata.py b/src/shared/shareddata.py index a82ab7f..2118378 100644 --- a/src/shared/shareddata.py +++ b/src/shared/shareddata.py @@ -13,7 +13,7 @@ import sys import platform -CURRENT_VERSION = "1.6.0" +CURRENT_VERSION = "1.6.1" APP_NAME = "flares" APP_NAME_EXPANDED = "fNIRS Lightweight Analysis, Research, & Evaluation Suite" API_URL = f"https://git.research.dezeeuw.ca/api/v1/repos/tyler/{APP_NAME}/releases"