From: Jakob Cornell Date: Sat, 28 Dec 2024 22:20:20 +0000 (-0600) Subject: Add macOS support and Pyinstaller spec file X-Git-Url: https://jcornell.net/gitweb/gitweb.cgi?a=commitdiff_plain;h=35c04ffcd233f93e56b29bb96cbe77fcea408ba0;p=tutoring-tool.git Add macOS support and Pyinstaller spec file --- diff --git a/main.py b/main.py index a8cf3cf..5128bc6 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,11 @@ from collections import defaultdict, namedtuple, OrderedDict from contextlib import contextmanager from enum import Enum +from matplotlib import pyplot import itertools import json -from matplotlib import pyplot +import matplotlib +import sys import tkinter as tk from tkinter import ttk @@ -54,6 +56,13 @@ def win_write_clipboard_html(text): clip.SetClipboardData(fmt, compute_payload(text.encode('utf-8'))) +def mac_write_clipboard_html(text: str) -> None: + from pasteboard import Pasteboard + import pasteboard + + Pasteboard().set_contents(text, type = pasteboard.HTML) + + TK_PAD = {'padx': 5, 'pady': 5} def center_on_parent(child, parent): @@ -239,6 +248,12 @@ class MainView(View): on_save_err() menu = tk.Menu(self.root) self.root.config(menu = menu) + + # Intercept macOS system quit (app menu/Cmd+Q). https://stackoverflow.com/a/72152310 + # Linux Tk doesn't seem to mind the createcommand call, so it's done on all platforms. + tk.Menu(menu, name = "apple") + self.root.createcommand("tk::mac::Quit", on_close) + file_menu = tk.Menu(menu, tearoff = 0) file_menu.add_command(label = "Save", command = on_save, accelerator = "Ctrl+S") menu.add_cascade(label = "File", menu = file_menu) @@ -354,7 +369,12 @@ class MainView(View): for v in row: SubElem(tr, 'td').text = v - win_write_clipboard_html(ElementTree.tostring(table, encoding = 'unicode')) + html_text = ElementTree.tostring(table, encoding = "unicode") + if sys.platform == "win32": + win_write_clipboard_html(html_text) + elif sys.platform == "darwin": + mac_write_clipboard_html(html_text) + self.gen_table_btn = ttk.Button(actions_frame, text = "Copy table to clipboard", command = on_gen_table) self.student_sel.pack(**TK_PAD, side = tk.LEFT) @@ -760,7 +780,10 @@ class AutoJsonStorageMgr: def __init__(self, name): import os.path, pathlib - path = pathlib.Path(os.path.expanduser('~'), 'AppData', self.UUID) + if sys.platform == "win32": + path = pathlib.Path(os.path.expanduser('~'), 'AppData', self.UUID) + else: + path = pathlib.Path(os.path.expanduser("~"), self.UUID) path.mkdir(parents = True, exist_ok = True) self.path = path.joinpath(name) @@ -777,6 +800,10 @@ class AutoJsonStorageMgr: json.dump(data, f) +# This seems to be necessary for Pyinstaller builds; not sure why. +if sys.platform == "darwin": + matplotlib.use("macosx") + model = MainModel(AutoJsonStorageMgr('data'), AutoJsonStorageMgr('state')) view = MainView() controller = MainController() diff --git a/tutoring_tool.spec b/tutoring_tool.spec new file mode 100644 index 0000000..fe664ca --- /dev/null +++ b/tutoring_tool.spec @@ -0,0 +1,51 @@ +# -*- mode: python ; coding: utf-8 -*- + + +a = Analysis( + ['main.py'], + pathex=[], + binaries=[], + datas=[], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, + optimize=0, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name='tutoring_tool', + debug=True, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=False, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon=['tt_icon.png'], # one of the RedNotebook.svg icons from Papirus +) +coll = COLLECT( + exe, + a.binaries, + a.datas, + strip=False, + upx=True, + upx_exclude=[], + name='Tutoring Tool', +) +app = BUNDLE( + coll, + name='Tutoring Tool.app', + icon='tt_icon.png', + bundle_identifier=None, +)