From 66b28fb81070676c286f0d1e0f11694791b04fe9 Mon Sep 17 00:00:00 2001 From: Nigel Date: Sun, 29 Dec 2024 20:37:27 +0000 Subject: [PATCH] Separate settings for checker dialogs Suspects-only and Sort method are now stored for each checker dialog. --- src/guiguts/application.py | 5 ++-- src/guiguts/checkers.py | 45 ++++++++++++++++++++--------- src/guiguts/illo_sn_fixup.py | 56 ++++++++++++++++++++++-------------- src/guiguts/preferences.py | 4 +-- src/guiguts/widgets.py | 43 +++++++++++++++++++++------ 5 files changed, 103 insertions(+), 50 deletions(-) diff --git a/src/guiguts/application.py b/src/guiguts/application.py index a7fe3997..51092a5e 100644 --- a/src/guiguts/application.py +++ b/src/guiguts/application.py @@ -12,7 +12,6 @@ import unicodedata import webbrowser -from guiguts.checkers import CheckerSortType from guiguts.data import themes from guiguts.file import File, the_file, NUM_RECENT_FILES from guiguts.footnotes import footnote_check, FootnoteIndexStyle @@ -354,8 +353,8 @@ def initialize_preferences(self) -> None: preferences.set_default(PrefKey.WFDIALOG_IGNORE_CASE, False) preferences.set_default(PrefKey.WFDIALOG_DISPLAY_TYPE, WFDisplayType.ALL_WORDS) preferences.set_default(PrefKey.WFDIALOG_SORT_TYPE, WFSortType.ALPHABETIC) - preferences.set_default(PrefKey.CHECKERDIALOG_SORT_TYPE, CheckerSortType.ROWCOL) - preferences.set_default(PrefKey.CHECKERDIALOG_SUSPECTS_ONLY, False) + preferences.set_default(PrefKey.CHECKERDIALOG_SORT_TYPE_DICT, {}) + preferences.set_default(PrefKey.CHECKERDIALOG_SUSPECTS_ONLY_DICT, {}) preferences.set_default(PrefKey.WFDIALOG_ITALIC_THRESHOLD, ["4"]) preferences.set_default(PrefKey.WFDIALOG_REGEX, []) preferences.set_default( diff --git a/src/guiguts/checkers.py b/src/guiguts/checkers.py index 3d6c2bf4..183c7a02 100644 --- a/src/guiguts/checkers.py +++ b/src/guiguts/checkers.py @@ -9,12 +9,7 @@ from guiguts.maintext import maintext from guiguts.mainwindow import ScrolledReadOnlyText -from guiguts.preferences import ( - PersistentString, - PersistentBoolean, - PrefKey, - preferences, -) +from guiguts.preferences import PrefKey from guiguts.root import root from guiguts.utilities import ( IndexRowCol, @@ -151,11 +146,22 @@ def __init__( self.count_label.grid(row=0, column=0, sticky="NSW") self.suspects_only_btn: Optional[ttk.Checkbutton] if show_suspects_only: + # Can't use a PersistentBoolean directly, since we save this value for each checker dialog + suspects_only_var = tk.BooleanVar( + self, self.get_dialog_pref(PrefKey.CHECKERDIALOG_SUSPECTS_ONLY_DICT) + ) + + def suspects_only_changed() -> None: + self.save_dialog_pref( + PrefKey.CHECKERDIALOG_SUSPECTS_ONLY_DICT, suspects_only_var.get() + ) + self.display_entries() + self.suspects_only_btn = ttk.Checkbutton( left_frame, text="Suspects Only", - variable=PersistentBoolean(PrefKey.CHECKERDIALOG_SUSPECTS_ONLY), - command=self.display_entries, + variable=suspects_only_var, + command=suspects_only_changed, takefocus=False, ) self.suspects_only_btn.grid(row=0, column=1, sticky="NSW", padx=(10, 0)) @@ -174,11 +180,22 @@ def copy_errors() -> None: left_frame, text="Sort:", ).grid(row=0, column=3, sticky="NSE", padx=5) - sort_type = PersistentString(PrefKey.CHECKERDIALOG_SORT_TYPE) + + # Can't use a PersistentString directly, since we save this value for each checker dialog + sort_type = tk.StringVar( + self, + self.get_dialog_pref(PrefKey.CHECKERDIALOG_SORT_TYPE_DICT) + or CheckerSortType.ROWCOL, + ) + + def sort_type_changed() -> None: + self.save_dialog_pref(PrefKey.CHECKERDIALOG_SORT_TYPE_DICT, sort_type.get()) + self.display_entries() + ttk.Radiobutton( left_frame, text="Line & Col", - command=self.display_entries, + command=sort_type_changed, variable=sort_type, value=CheckerSortType.ROWCOL, takefocus=False, @@ -186,7 +203,7 @@ def copy_errors() -> None: ttk.Radiobutton( left_frame, text="Alpha/Type", - command=self.display_entries, + command=sort_type_changed, variable=sort_type, value=CheckerSortType.ALPHABETIC, takefocus=False, @@ -486,7 +503,7 @@ def display_entries(self, auto_select_line: bool = True) -> None: Busy.busy() sort_key: Callable[[CheckerEntry], tuple] if ( - preferences.get(PrefKey.CHECKERDIALOG_SORT_TYPE) + self.get_dialog_pref(PrefKey.CHECKERDIALOG_SORT_TYPE_DICT) == CheckerSortType.ALPHABETIC ): sort_key = self.alpha_key @@ -589,8 +606,8 @@ def showing_suspects_only(self) -> bool: Returns: True if there's a Suspects Only button that is switched on. """ - return self.suspects_only_btn is not None and preferences.get( - PrefKey.CHECKERDIALOG_SUSPECTS_ONLY + return self.suspects_only_btn is not None and self.get_dialog_pref( + PrefKey.CHECKERDIALOG_SUSPECTS_ONLY_DICT ) def skip_suspect_entry(self, entry: CheckerEntry) -> bool: diff --git a/src/guiguts/illo_sn_fixup.py b/src/guiguts/illo_sn_fixup.py index e1d49af1..241cc4b1 100644 --- a/src/guiguts/illo_sn_fixup.py +++ b/src/guiguts/illo_sn_fixup.py @@ -15,7 +15,8 @@ logger = logging.getLogger(__package__) -_the_illosn_checker: Optional["IlloSNChecker"] = None # pylint: disable=invalid-name +_the_illo_checker: Optional["IlloSNChecker"] = None # pylint: disable=invalid-name +_the_sn_checker: Optional["IlloSNChecker"] = None # pylint: disable=invalid-name class IlloSNRecord: @@ -96,9 +97,8 @@ def get_selected_illosn_record( Line number index of first line of the record; e.g. '5.0' Line number index of last line of the record; e.g. '6.14' """ - assert _the_illosn_checker is not None # We built a list of Illo or SN records with start/end RowCol details. - records = _the_illosn_checker.get_illosn_records() + records = self.get_illosn_records() # Get record of the selected tag selected_record = records[selected_illosn_index] # Get line number index of the first line of the selected record. @@ -116,14 +116,13 @@ def get_selected_illosn_index(self) -> int: Returns: Index into self.illosn_records array, negative if none selected.""" - assert _the_illosn_checker is not None cur_idx = self.checker_dialog.current_entry_index() if cur_idx is None: return -1 text_range = self.checker_dialog.entries[cur_idx].text_range assert text_range is not None illosn_start = text_range.start - illosn_records = _the_illosn_checker.get_illosn_records() + illosn_records = self.get_illosn_records() for illosn_index, illosn_record in enumerate(illosn_records): if illosn_record.start == illosn_start: return illosn_index @@ -286,7 +285,7 @@ def update_after_move(self, tag_type: str, selected_illosn_index: int) -> None: # Update illosn_records list self.run_check(tag_type) # Update dialog - display_illosn_entries() + display_illosn_entries(tag_type) # Select again the tag we have just moved so it is highlighted. self.checker_dialog.select_entry_by_index(selected_illosn_index) @@ -414,7 +413,6 @@ def move_selection_up(self, tag_type: str) -> None: Args: tag_type: a string which is either "Sidenote" or "Illustration". """ - assert _the_illosn_checker is not None selected_illosn_index = self.get_selected_illosn_index() if selected_illosn_index < 0: return # No selection. @@ -503,7 +501,6 @@ def move_selection_down(self, tag_type: str) -> None: Args: tag_type: a string which is either "Sidenote" or "Illustration". """ - assert _the_illosn_checker is not None selected_illosn_index = self.get_selected_illosn_index() if selected_illosn_index < 0: return # No selection @@ -587,7 +584,8 @@ def illosn_check(tag_type: str) -> None: Args: tag_type: which tag to check for - "Illustration" or "Sidenote" """ - global _the_illosn_checker + global _the_illo_checker + global _the_sn_checker if not tool_save(): return @@ -598,10 +596,18 @@ def illosn_check(tag_type: str) -> None: rerun_command=lambda: illosn_check(tag_type), show_suspects_only=True, ) - if _the_illosn_checker is None: - _the_illosn_checker = IlloSNChecker(checker_dialog) - elif not _the_illosn_checker.checker_dialog.winfo_exists(): - _the_illosn_checker.checker_dialog = checker_dialog + if tag_type == "Illustration": + if _the_illo_checker is None: + _the_illo_checker = IlloSNChecker(checker_dialog) + elif not _the_illo_checker.checker_dialog.winfo_exists(): + _the_illo_checker.checker_dialog = checker_dialog + the_checker = _the_illo_checker + else: + if _the_sn_checker is None: + _the_sn_checker = IlloSNChecker(checker_dialog) + elif not _the_sn_checker.checker_dialog.winfo_exists(): + _the_sn_checker.checker_dialog = checker_dialog + the_checker = _the_sn_checker ToolTip( checker_dialog.text, @@ -620,23 +626,29 @@ def illosn_check(tag_type: str) -> None: ttk.Button( frame, text="Move Selection Up", - command=lambda: _the_illosn_checker.move_selection_up(tag_type), + command=lambda: the_checker.move_selection_up(tag_type), ).grid(column=0, row=0, sticky="NSW") ttk.Button( frame, text="Move Selection Down", - command=lambda: _the_illosn_checker.move_selection_down(tag_type), + command=lambda: the_checker.move_selection_down(tag_type), ).grid(column=1, row=0, sticky="NSW") - _the_illosn_checker.run_check(tag_type) - display_illosn_entries() + the_checker.run_check(tag_type) + display_illosn_entries(tag_type) -def display_illosn_entries() -> None: - """(Re-)display the requested Illo/SN tag types in the checker dialog.""" - assert _the_illosn_checker is not None - checker_dialog = _the_illosn_checker.checker_dialog +def display_illosn_entries(tag_type: str) -> None: + """(Re-)display the requested Illo/SN tag types in the checker dialog. + + Args: + tag_type: which tag to check for - "Illustration" or "Sidenote" + """ + assert tag_type in ("Illustration", "Sidenote") + the_checker = _the_illo_checker if tag_type == "Illustration" else _the_sn_checker + assert the_checker is not None + checker_dialog = the_checker.checker_dialog checker_dialog.reset() - illosn_records = _the_illosn_checker.get_illosn_records() + illosn_records = the_checker.get_illosn_records() for illosn_record in illosn_records: error_prefix = "" if illosn_record.mid_para: diff --git a/src/guiguts/preferences.py b/src/guiguts/preferences.py index c4eb42e3..a6b8746f 100644 --- a/src/guiguts/preferences.py +++ b/src/guiguts/preferences.py @@ -41,8 +41,8 @@ class PrefKey(StrEnum): WFDIALOG_SORT_TYPE = auto() WFDIALOG_ITALIC_THRESHOLD = auto() WFDIALOG_REGEX = auto() - CHECKERDIALOG_SORT_TYPE = auto() - CHECKERDIALOG_SUSPECTS_ONLY = auto() + CHECKERDIALOG_SORT_TYPE_DICT = auto() + CHECKERDIALOG_SUSPECTS_ONLY_DICT = auto() DIALOG_GEOMETRY = auto() ROOT_GEOMETRY = auto() ROOT_GEOMETRY_STATE = auto() diff --git a/src/guiguts/widgets.py b/src/guiguts/widgets.py index 24a7fb68..80799a6e 100644 --- a/src/guiguts/widgets.py +++ b/src/guiguts/widgets.py @@ -242,11 +242,7 @@ def _get_pref_geometry(self) -> str: Returns: String containing geometry, or empty string if none stored. """ - config_dict = preferences.get(PrefKey.DIALOG_GEOMETRY) - try: - return config_dict[self.__class__.__name__] - except KeyError: - return "" + return self.get_dialog_pref(PrefKey.DIALOG_GEOMETRY) def allow_geometry_save(self) -> None: """Enable the saving of geometry changes via Configure events. @@ -274,12 +270,41 @@ def _save_config(self) -> None: dialog creation and resizing. Only the first will actually do a save, because the flag will only be true on the first call.""" if self.save_config: - config_dict = preferences.get(PrefKey.DIALOG_GEOMETRY) - key = self.__class__.__name__ - config_dict[key] = self.geometry() - preferences.set(PrefKey.DIALOG_GEOMETRY, config_dict) + self.save_dialog_pref(PrefKey.DIALOG_GEOMETRY, self.geometry()) self.save_config = False + def save_dialog_pref(self, key: PrefKey, value: Any) -> None: + """Save a preference that is unique to the dialog class. + + Dictionary indexed by classname is saved in the prefs file, + so a preference can be saved per dialog. + + Args: + key: Preference to be saved. + value: New value for preference. + """ + config_dict = preferences.get(key) + config_dict[self.__class__.__name__] = value + preferences.set(key, config_dict) + + def get_dialog_pref(self, key: PrefKey) -> Any: + """Get a preference that is unique to the dialog class. + + Dictionary in prefs file is indexed by classname, + so a preference can be obtained per dialog. + + Args: + key: Preference to be fetched. + + Returns: + Preference value. + """ + config_dict = preferences.get(key) + try: + return config_dict[self.__class__.__name__] + except KeyError: + return None + class OkApplyCancelDialog(ToplevelDialog): """A ToplevelDialog with OK, Apply & Cancel buttons."""