backlogops-gui 0.6__tar.gz → 0.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {backlogops_gui-0.6 → backlogops_gui-0.8}/PKG-INFO +7 -6
- {backlogops_gui-0.6 → backlogops_gui-0.8}/README_pypi.md +2 -2
- {backlogops_gui-0.6 → backlogops_gui-0.8}/backlogops_gui.egg-info/PKG-INFO +7 -6
- {backlogops_gui-0.6 → backlogops_gui-0.8}/backlogops_gui.egg-info/SOURCES.txt +1 -13
- backlogops_gui-0.8/backlogops_gui.egg-info/requires.txt +5 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/setup.py +5 -4
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/application.py +2 -2
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/backlog_dialogs.py +1 -1
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/backlog_window.py +2 -2
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/choice_dialogs.py +1 -1
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/format_dialogs.py +1 -1
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/jira_dialogs.py +1 -1
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/key_list_box.py +1 -1
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/modal_dialog.py +2 -2
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/report_windows.py +1 -1
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/token_dialog.py +1 -1
- backlogops_gui-0.6/backlogops_gui.egg-info/requires.txt +0 -4
- backlogops_gui-0.6/src/backlogops_gui/auto_scroll.py +0 -37
- backlogops_gui-0.6/src/backlogops_gui/close_binding.py +0 -47
- backlogops_gui-0.6/src/backlogops_gui/gui_style.py +0 -70
- backlogops_gui-0.6/src/backlogops_gui/gui_wizard.py +0 -138
- backlogops_gui-0.6/src/backlogops_gui/wizard_calendar.py +0 -170
- backlogops_gui-0.6/src/backlogops_gui/wizard_form.py +0 -670
- backlogops_gui-0.6/src/backlogops_gui/wizard_path.py +0 -177
- backlogops_gui-0.6/src/backlogops_gui/wizard_pick_row.py +0 -154
- backlogops_gui-0.6/src/backlogops_gui/wizard_prefill.py +0 -138
- backlogops_gui-0.6/src/backlogops_gui/wizard_table.py +0 -259
- backlogops_gui-0.6/src/backlogops_gui/wizard_typed.py +0 -286
- backlogops_gui-0.6/src/backlogops_gui/wizard_window.py +0 -364
- {backlogops_gui-0.6 → backlogops_gui-0.8}/LICENSE.txt +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/backlogops_gui.egg-info/dependency_links.txt +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/backlogops_gui.egg-info/entry_points.txt +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/backlogops_gui.egg-info/top_level.txt +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/pyproject.toml +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/setup.cfg +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/__init__.py +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/__main__.py +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/_migrate_warn.py +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/backlog_actions.py +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/backlog_io.py +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/blog_version_reporter.py +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/file_choosers.py +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/jira_actions.py +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/jira_base.py +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/jira_order.py +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/jira_rank.py +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/jira_read.py +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/jira_rename.py +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/jira_update.py +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/jira_write.py +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/log_buffer.py +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/py.typed +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/python_version.py +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/table_view.py +0 -0
- {backlogops_gui-0.6 → backlogops_gui-0.8}/src/backlogops_gui/tcltk_version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: backlogops-gui
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8
|
|
4
4
|
Summary: Graphical user interface for backlog operations.
|
|
5
5
|
Author: Tom Björkholm
|
|
6
6
|
Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
|
|
@@ -21,9 +21,10 @@ Classifier: Typing :: Typed
|
|
|
21
21
|
Requires-Python: >=3.12
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
License-File: LICENSE.txt
|
|
24
|
-
Requires-Dist: backlogops>=0.
|
|
25
|
-
Requires-Dist: argcomplete>=3.7.
|
|
26
|
-
Requires-Dist: wizard-ui-bridge>=1.
|
|
24
|
+
Requires-Dist: backlogops>=0.8
|
|
25
|
+
Requires-Dist: argcomplete>=3.7.1
|
|
26
|
+
Requires-Dist: wizard-ui-bridge>=1.3
|
|
27
|
+
Requires-Dist: wizard-tk-bridge>=1.3
|
|
27
28
|
Requires-Dist: versionreporter>=0.4
|
|
28
29
|
Dynamic: author
|
|
29
30
|
Dynamic: license-file
|
|
@@ -349,10 +350,10 @@ one with the list of releases. You will want to use the menus.
|
|
|
349
350
|
|
|
350
351
|
## Test summary
|
|
351
352
|
|
|
352
|
-
- Test result:
|
|
353
|
+
- Test result: 2173 passed, 1 deselected in 35s
|
|
353
354
|
- No flake8 warnings.
|
|
354
355
|
- No mypy errors found.
|
|
355
356
|
- No pylint warnings.
|
|
356
357
|
- No python layout warnings.
|
|
357
|
-
- Built version(s): 0.
|
|
358
|
+
- Built version(s): 0.8
|
|
358
359
|
- Build and test using Python 3.14.6
|
|
@@ -317,10 +317,10 @@ one with the list of releases. You will want to use the menus.
|
|
|
317
317
|
|
|
318
318
|
## Test summary
|
|
319
319
|
|
|
320
|
-
- Test result:
|
|
320
|
+
- Test result: 2173 passed, 1 deselected in 35s
|
|
321
321
|
- No flake8 warnings.
|
|
322
322
|
- No mypy errors found.
|
|
323
323
|
- No pylint warnings.
|
|
324
324
|
- No python layout warnings.
|
|
325
|
-
- Built version(s): 0.
|
|
325
|
+
- Built version(s): 0.8
|
|
326
326
|
- Build and test using Python 3.14.6
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: backlogops-gui
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8
|
|
4
4
|
Summary: Graphical user interface for backlog operations.
|
|
5
5
|
Author: Tom Björkholm
|
|
6
6
|
Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
|
|
@@ -21,9 +21,10 @@ Classifier: Typing :: Typed
|
|
|
21
21
|
Requires-Python: >=3.12
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
License-File: LICENSE.txt
|
|
24
|
-
Requires-Dist: backlogops>=0.
|
|
25
|
-
Requires-Dist: argcomplete>=3.7.
|
|
26
|
-
Requires-Dist: wizard-ui-bridge>=1.
|
|
24
|
+
Requires-Dist: backlogops>=0.8
|
|
25
|
+
Requires-Dist: argcomplete>=3.7.1
|
|
26
|
+
Requires-Dist: wizard-ui-bridge>=1.3
|
|
27
|
+
Requires-Dist: wizard-tk-bridge>=1.3
|
|
27
28
|
Requires-Dist: versionreporter>=0.4
|
|
28
29
|
Dynamic: author
|
|
29
30
|
Dynamic: license-file
|
|
@@ -349,10 +350,10 @@ one with the list of releases. You will want to use the menus.
|
|
|
349
350
|
|
|
350
351
|
## Test summary
|
|
351
352
|
|
|
352
|
-
- Test result:
|
|
353
|
+
- Test result: 2173 passed, 1 deselected in 35s
|
|
353
354
|
- No flake8 warnings.
|
|
354
355
|
- No mypy errors found.
|
|
355
356
|
- No pylint warnings.
|
|
356
357
|
- No python layout warnings.
|
|
357
|
-
- Built version(s): 0.
|
|
358
|
+
- Built version(s): 0.8
|
|
358
359
|
- Build and test using Python 3.14.6
|
|
@@ -12,18 +12,14 @@ src/backlogops_gui/__init__.py
|
|
|
12
12
|
src/backlogops_gui/__main__.py
|
|
13
13
|
src/backlogops_gui/_migrate_warn.py
|
|
14
14
|
src/backlogops_gui/application.py
|
|
15
|
-
src/backlogops_gui/auto_scroll.py
|
|
16
15
|
src/backlogops_gui/backlog_actions.py
|
|
17
16
|
src/backlogops_gui/backlog_dialogs.py
|
|
18
17
|
src/backlogops_gui/backlog_io.py
|
|
19
18
|
src/backlogops_gui/backlog_window.py
|
|
20
19
|
src/backlogops_gui/blog_version_reporter.py
|
|
21
20
|
src/backlogops_gui/choice_dialogs.py
|
|
22
|
-
src/backlogops_gui/close_binding.py
|
|
23
21
|
src/backlogops_gui/file_choosers.py
|
|
24
22
|
src/backlogops_gui/format_dialogs.py
|
|
25
|
-
src/backlogops_gui/gui_style.py
|
|
26
|
-
src/backlogops_gui/gui_wizard.py
|
|
27
23
|
src/backlogops_gui/jira_actions.py
|
|
28
24
|
src/backlogops_gui/jira_base.py
|
|
29
25
|
src/backlogops_gui/jira_dialogs.py
|
|
@@ -41,12 +37,4 @@ src/backlogops_gui/python_version.py
|
|
|
41
37
|
src/backlogops_gui/report_windows.py
|
|
42
38
|
src/backlogops_gui/table_view.py
|
|
43
39
|
src/backlogops_gui/tcltk_version.py
|
|
44
|
-
src/backlogops_gui/token_dialog.py
|
|
45
|
-
src/backlogops_gui/wizard_calendar.py
|
|
46
|
-
src/backlogops_gui/wizard_form.py
|
|
47
|
-
src/backlogops_gui/wizard_path.py
|
|
48
|
-
src/backlogops_gui/wizard_pick_row.py
|
|
49
|
-
src/backlogops_gui/wizard_prefill.py
|
|
50
|
-
src/backlogops_gui/wizard_table.py
|
|
51
|
-
src/backlogops_gui/wizard_typed.py
|
|
52
|
-
src/backlogops_gui/wizard_window.py
|
|
40
|
+
src/backlogops_gui/token_dialog.py
|
|
@@ -5,7 +5,7 @@ from setuptools import setup
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name='backlogops-gui',
|
|
8
|
-
version='0.
|
|
8
|
+
version='0.8',
|
|
9
9
|
description='Graphical user interface for backlog operations.',
|
|
10
10
|
author='Tom Björkholm',
|
|
11
11
|
author_email='klausuler_linnet0q@icloud.com',
|
|
@@ -19,9 +19,10 @@ setup(
|
|
|
19
19
|
]
|
|
20
20
|
},
|
|
21
21
|
install_requires=[
|
|
22
|
-
'backlogops >= 0.
|
|
23
|
-
'argcomplete >= 3.7.
|
|
24
|
-
'wizard-ui-bridge >= 1.
|
|
22
|
+
'backlogops >= 0.8',
|
|
23
|
+
'argcomplete >= 3.7.1',
|
|
24
|
+
'wizard-ui-bridge >= 1.3',
|
|
25
|
+
'wizard-tk-bridge >= 1.3',
|
|
25
26
|
'versionreporter >= 0.4'
|
|
26
27
|
]
|
|
27
28
|
)
|
|
@@ -39,6 +39,7 @@ from typing import Callable, Optional, TextIO, TypeVar
|
|
|
39
39
|
import argcomplete
|
|
40
40
|
from config_as_json import Config, migrate_cfg
|
|
41
41
|
from config_as_json.file_extension import fix_file_extension
|
|
42
|
+
from wizard_tk_bridge import WizardUiBridgeTk
|
|
42
43
|
from backlogops import (
|
|
43
44
|
AvailableTeams, BacklogOpsConfig, BacklogReleases, GuiDisplayConfig,
|
|
44
45
|
InputFormatConfig, Levels, OutputFormatConfig, Status, get_demo_backlog,
|
|
@@ -49,7 +50,6 @@ from backlogops_gui.backlog_io import read_backlog
|
|
|
49
50
|
from backlogops_gui.backlog_window import (
|
|
50
51
|
BacklogWindow, BacklogSource, JiraHandlers, current_time)
|
|
51
52
|
from backlogops_gui.blog_version_reporter import BloGuiVersionReporter
|
|
52
|
-
from backlogops_gui.gui_wizard import TkWizardBridge
|
|
53
53
|
from backlogops_gui.choice_dialogs import (
|
|
54
54
|
ConfigChoice, PresetKind, SourceChoice, ask_no_config_choice,
|
|
55
55
|
ask_preset_kind, ask_source_choice)
|
|
@@ -273,7 +273,7 @@ class BacklogApp:
|
|
|
273
273
|
wizard failure is reported under ``error_title`` and also yields
|
|
274
274
|
None. The bridge window is always closed afterwards.
|
|
275
275
|
"""
|
|
276
|
-
bridge =
|
|
276
|
+
bridge = WizardUiBridgeTk(self.root, log=self.log)
|
|
277
277
|
try:
|
|
278
278
|
return wizard(bridge, default=default)
|
|
279
279
|
except EOFError:
|
|
@@ -17,8 +17,8 @@ from tkinter import messagebox, ttk
|
|
|
17
17
|
from dataclasses import dataclass
|
|
18
18
|
from datetime import date
|
|
19
19
|
from typing import Optional, TextIO
|
|
20
|
+
from wizard_tk_bridge.gui_style import style_input
|
|
20
21
|
from backlogops import DependencyMode, DEFAULT_LEVELS
|
|
21
|
-
from backlogops_gui.gui_style import style_input
|
|
22
22
|
from backlogops_gui.key_list_box import build_key_box, load_keys_into
|
|
23
23
|
from backlogops_gui.modal_dialog import ModalDialog
|
|
24
24
|
|
|
@@ -22,18 +22,18 @@ from pathlib import Path
|
|
|
22
22
|
from tkinter import messagebox, ttk
|
|
23
23
|
from typing import Callable, Literal, Optional, TextIO
|
|
24
24
|
from tableio import ValueFmt
|
|
25
|
+
from wizard_tk_bridge.auto_scroll import auto_hide
|
|
26
|
+
from wizard_tk_bridge.close_binding import CLOSE_ACCELERATOR, bind_close
|
|
25
27
|
from backlogops import (
|
|
26
28
|
AddedReleasesToJira, AddedToJira, AvailableTeams, BacklogReleases,
|
|
27
29
|
GuiDisplayConfig, Levels, OrderedReleasesInJira, OutputFormatConfig,
|
|
28
30
|
RankedInJira, RenamedReleasesInJira, UpdatedBacklogInJira,
|
|
29
31
|
UpdatedReleasesInJira, format_order_result, format_rank_result,
|
|
30
32
|
format_release_result, format_release_updates, format_rename_result)
|
|
31
|
-
from backlogops_gui.auto_scroll import auto_hide
|
|
32
33
|
from backlogops_gui.backlog_actions import (
|
|
33
34
|
adjust_content, apply_add_result, apply_update_result, estimate_date,
|
|
34
35
|
extract_keys, order_by_deps, order_by_keys, order_by_release, order_dates,
|
|
35
36
|
plan_dates, save_backlog, set_plan)
|
|
36
|
-
from backlogops_gui.close_binding import CLOSE_ACCELERATOR, bind_close
|
|
37
37
|
from backlogops_gui.report_windows import show_text_report
|
|
38
38
|
from backlogops_gui.table_view import (
|
|
39
39
|
backlog_table, make_table, release_table)
|
|
@@ -16,7 +16,7 @@ three are built from the same :class:`ButtonChoiceDialog`.
|
|
|
16
16
|
import tkinter as tk
|
|
17
17
|
from enum import Enum
|
|
18
18
|
from typing import Generic, Optional, Sequence, TypeVar
|
|
19
|
-
from
|
|
19
|
+
from wizard_tk_bridge.close_binding import bind_close
|
|
20
20
|
|
|
21
21
|
CHOICE_WRAP = 360
|
|
22
22
|
NO_CONFIG_TEXT = (
|
|
@@ -16,7 +16,7 @@ import tkinter as tk
|
|
|
16
16
|
from tkinter import filedialog, ttk
|
|
17
17
|
from dataclasses import dataclass
|
|
18
18
|
from typing import Optional, Sequence
|
|
19
|
-
from
|
|
19
|
+
from wizard_tk_bridge.gui_style import style_input
|
|
20
20
|
from backlogops_gui.modal_dialog import ModalDialog
|
|
21
21
|
|
|
22
22
|
MODE_INFER = 0
|
|
@@ -22,8 +22,8 @@ from tkinter import messagebox, ttk
|
|
|
22
22
|
from dataclasses import dataclass
|
|
23
23
|
from collections.abc import Mapping
|
|
24
24
|
from typing import Optional, Sequence, TextIO
|
|
25
|
+
from wizard_tk_bridge.gui_style import style_input
|
|
25
26
|
from backlogops import JiraRankAnchor, OnMissingKey, ReleaseRename
|
|
26
|
-
from backlogops_gui.gui_style import style_input
|
|
27
27
|
from backlogops_gui.key_list_box import build_key_box, load_keys_into
|
|
28
28
|
from backlogops_gui.modal_dialog import ModalDialog
|
|
29
29
|
|
|
@@ -13,8 +13,8 @@ import tkinter as tk
|
|
|
13
13
|
from tkinter import filedialog, messagebox
|
|
14
14
|
from collections.abc import Callable
|
|
15
15
|
from typing import TextIO
|
|
16
|
+
from wizard_tk_bridge.gui_style import style_input
|
|
16
17
|
from backlogops import read_key_list
|
|
17
|
-
from backlogops_gui.gui_style import style_input
|
|
18
18
|
|
|
19
19
|
KEY_READ_ERRORS = (ValueError, TypeError, KeyError, OSError)
|
|
20
20
|
|
|
@@ -12,8 +12,8 @@ store the entered values before the window closes.
|
|
|
12
12
|
# MIT License
|
|
13
13
|
|
|
14
14
|
import tkinter as tk
|
|
15
|
-
from
|
|
16
|
-
from
|
|
15
|
+
from wizard_tk_bridge.close_binding import bind_close
|
|
16
|
+
from wizard_tk_bridge.gui_style import focus_first_input
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
# pylint: disable-next=too-few-public-methods
|
|
@@ -12,7 +12,7 @@ created window so a caller or a test can drive or close it.
|
|
|
12
12
|
|
|
13
13
|
import tkinter as tk
|
|
14
14
|
from typing import Callable
|
|
15
|
-
from
|
|
15
|
+
from wizard_tk_bridge.close_binding import bind_close
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def show_change_list(parent: tk.Misc, title: str, text: str,
|
|
@@ -19,7 +19,7 @@ from tkinter import filedialog, messagebox
|
|
|
19
19
|
from dataclasses import dataclass
|
|
20
20
|
from pathlib import Path
|
|
21
21
|
from typing import Callable, Optional
|
|
22
|
-
from
|
|
22
|
+
from wizard_tk_bridge.gui_style import style_input
|
|
23
23
|
from backlogops_gui.modal_dialog import ModalDialog
|
|
24
24
|
|
|
25
25
|
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
#! /usr/local/bin/python3
|
|
2
|
-
"""A scroll command that shows a scrollbar only while it can scroll.
|
|
3
|
-
|
|
4
|
-
A table that fits its area needs no scrollbar, and a scrollbar that is
|
|
5
|
-
always shown wastes space and hints at hidden content that is not there.
|
|
6
|
-
:func:`auto_hide` returns the scroll command for a scrolling widget: the
|
|
7
|
-
command hides the scrollbar while the whole range is visible and shows it
|
|
8
|
-
again once the widget grows past its area. It works for any widget that
|
|
9
|
-
reports its position through an ``xscrollcommand`` or ``yscrollcommand``,
|
|
10
|
-
so the wizard table canvas and the read-only backlog tree can share it.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
# Copyright (c) 2026, Tom Björkholm
|
|
14
|
-
# MIT License
|
|
15
|
-
|
|
16
|
-
from tkinter import ttk
|
|
17
|
-
from typing import Callable
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def auto_hide(scrollbar: ttk.Scrollbar
|
|
21
|
-
) -> Callable[[float | str, float | str], None]:
|
|
22
|
-
"""Return a scroll command that hides the scrollbar when it is full.
|
|
23
|
-
|
|
24
|
-
The result is used as a widget's ``xscrollcommand`` or
|
|
25
|
-
``yscrollcommand``. Tk reports the position as two fractions, which it
|
|
26
|
-
passes as strings, so the command accepts either a number or its
|
|
27
|
-
string form. The scrollbar must be laid out with the grid manager,
|
|
28
|
-
whose ``grid_remove`` remembers its cell across the hide.
|
|
29
|
-
"""
|
|
30
|
-
def command(first: float | str, last: float | str) -> None:
|
|
31
|
-
"""Hide or show the scrollbar, then move its thumb to the view."""
|
|
32
|
-
if float(first) <= 0.0 and float(last) >= 1.0:
|
|
33
|
-
scrollbar.grid_remove()
|
|
34
|
-
else:
|
|
35
|
-
scrollbar.grid()
|
|
36
|
-
scrollbar.set(first, last)
|
|
37
|
-
return command
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
#! /usr/local/bin/python3
|
|
2
|
-
"""Bind the close-window key to a secondary window's close action.
|
|
3
|
-
|
|
4
|
-
On macOS the Tk toolkit does not close a window when the user presses
|
|
5
|
-
Cmd-W, so every secondary window binds that key here. Cmd-W is bound on
|
|
6
|
-
every platform, where it is harmless without a Command key, and Ctrl-W is
|
|
7
|
-
added on Windows, its customary close-window shortcut. The bound action
|
|
8
|
-
defaults to destroying the window but may be the window's own cancel or
|
|
9
|
-
abort handler, so the key behaves exactly like the window close button.
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
# Copyright (c) 2026, Tom Björkholm
|
|
13
|
-
# MIT License
|
|
14
|
-
|
|
15
|
-
import sys
|
|
16
|
-
import tkinter as tk
|
|
17
|
-
from typing import Callable, Optional
|
|
18
|
-
|
|
19
|
-
CLOSE_ACCELERATOR = 'Command-W'
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def _close_events() -> list[str]:
|
|
23
|
-
"""Return the key patterns that close a window on this platform."""
|
|
24
|
-
if sys.platform.startswith('win'):
|
|
25
|
-
return ['<Command-w>', '<Control-w>']
|
|
26
|
-
return ['<Command-w>']
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def _perform_close(win: tk.Toplevel,
|
|
30
|
-
on_close: Optional[Callable[[], None]]) -> str:
|
|
31
|
-
"""Run the window's close action and stop further event handling."""
|
|
32
|
-
(win.destroy if on_close is None else on_close)()
|
|
33
|
-
return 'break'
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def bind_close(win: tk.Toplevel,
|
|
37
|
-
on_close: Optional[Callable[[], None]] = None) -> None:
|
|
38
|
-
"""Bind the close-window key to run the window's close action.
|
|
39
|
-
|
|
40
|
-
Args:
|
|
41
|
-
win: The secondary window to close on the key press.
|
|
42
|
-
on_close: The close action, defaulting to destroying the window.
|
|
43
|
-
A window that cancels or aborts on close passes its own
|
|
44
|
-
handler, so the key matches its window close button.
|
|
45
|
-
"""
|
|
46
|
-
for event in _close_events():
|
|
47
|
-
win.bind(event, lambda _event: _perform_close(win, on_close))
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
#! /usr/local/bin/python3
|
|
2
|
-
"""Shared look and focus helpers for the Tkinter input windows.
|
|
3
|
-
|
|
4
|
-
Editable input widgets blend into the window background on some
|
|
5
|
-
platforms, so the user cannot tell an entry, drop-down or list from the
|
|
6
|
-
surrounding window. :func:`style_input` gives such a widget a white
|
|
7
|
-
field and a thin border so it stands out. :func:`focus_first_input`
|
|
8
|
-
puts the keyboard focus on the first editable widget of a window, so the
|
|
9
|
-
user can start typing as soon as the window opens.
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
# Copyright (c) 2026, Tom Björkholm
|
|
13
|
-
# MIT License
|
|
14
|
-
|
|
15
|
-
import tkinter as tk
|
|
16
|
-
from tkinter import ttk
|
|
17
|
-
from typing import Optional
|
|
18
|
-
|
|
19
|
-
INPUT_BG = 'white'
|
|
20
|
-
INPUT_STYLE = 'Input.TCombobox'
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def style_input(widget: tk.Widget) -> None:
|
|
24
|
-
"""Make one editable input widget stand out from the background.
|
|
25
|
-
|
|
26
|
-
A classic entry, text box or list gets a white field and a thin
|
|
27
|
-
solid border. A drop-down keeps its arrow but gets a white field
|
|
28
|
-
through a shared ttk style. Any other widget is left unchanged. The
|
|
29
|
-
ttk styling is best-effort: a native theme that ignores field colors
|
|
30
|
-
leaves the drop-down as it is.
|
|
31
|
-
"""
|
|
32
|
-
if isinstance(widget, ttk.Widget):
|
|
33
|
-
if isinstance(widget, ttk.Combobox):
|
|
34
|
-
_style_combobox(widget)
|
|
35
|
-
return
|
|
36
|
-
if isinstance(widget, (tk.Entry, tk.Text, tk.Listbox)):
|
|
37
|
-
widget.configure(background=INPUT_BG, relief='solid', borderwidth=1)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def _style_combobox(widget: ttk.Combobox) -> None:
|
|
41
|
-
"""Give a drop-down a white field through a shared ttk style."""
|
|
42
|
-
style = ttk.Style(widget)
|
|
43
|
-
style.configure(INPUT_STYLE, fieldbackground=INPUT_BG)
|
|
44
|
-
style.map(INPUT_STYLE, fieldbackground=[('readonly', INPUT_BG)])
|
|
45
|
-
widget.configure(style=INPUT_STYLE)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def focus_first_input(window: tk.Misc) -> None:
|
|
49
|
-
"""Give the keyboard focus to the first editable input, if any."""
|
|
50
|
-
target = _first_input(window)
|
|
51
|
-
if target is not None:
|
|
52
|
-
target.focus_set()
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def _first_input(parent: tk.Misc) -> Optional[tk.Misc]:
|
|
56
|
-
"""Return the first editable input under parent, in child order."""
|
|
57
|
-
for child in parent.winfo_children():
|
|
58
|
-
if _is_input(child):
|
|
59
|
-
return child
|
|
60
|
-
found = _first_input(child)
|
|
61
|
-
if found is not None:
|
|
62
|
-
return found
|
|
63
|
-
return None
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def _is_input(widget: tk.Misc) -> bool:
|
|
67
|
-
"""Return whether the widget is an editable input to fill in."""
|
|
68
|
-
if not isinstance(widget, (tk.Entry, tk.Listbox, tk.Text)):
|
|
69
|
-
return False
|
|
70
|
-
return str(widget.cget('state')) != 'disabled'
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
#! /usr/local/bin/python3
|
|
2
|
-
"""Graphical bridge that drives the synchronous config wizard.
|
|
3
|
-
|
|
4
|
-
The backlog-ops configuration wizard asks its questions through a
|
|
5
|
-
:class:`WizardUiBridge`. This module provides :class:`TkWizardBridge`, a
|
|
6
|
-
concrete bridge that overrides every typed ask method of that base class
|
|
7
|
-
with a real Tkinter control, including the GUI-recommended ones: ask_path()
|
|
8
|
-
opens a native file or directory picker, and ask_form() shows a whole form
|
|
9
|
-
on one screen so the user answers related fields together in any order. All
|
|
10
|
-
questions are answered in one reused
|
|
11
|
-
:class:`~backlogops_gui.wizard_window.WizardWindow`, so the whole wizard
|
|
12
|
-
session happens in a single pop-up that does not jump around the display.
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
# Copyright (c) 2026, Tom Björkholm
|
|
16
|
-
# MIT License
|
|
17
|
-
|
|
18
|
-
from typing import Optional, Sequence, TextIO
|
|
19
|
-
from pathlib import Path
|
|
20
|
-
import tkinter as tk
|
|
21
|
-
from wizard_ui_bridge import AnswerFields, AskField, AskFields, PartialCheck, \
|
|
22
|
-
PartialFormValidator, PathAskOptions, TableCell, TableColumn, \
|
|
23
|
-
WizardUiBridge
|
|
24
|
-
from backlogops import NoTextIO
|
|
25
|
-
from backlogops_gui.wizard_form import handles_field
|
|
26
|
-
from backlogops_gui.wizard_window import WizardWindow
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class TkWizardBridge(WizardUiBridge):
|
|
30
|
-
"""Bridge that answers wizard prompts in one reused Tkinter window."""
|
|
31
|
-
|
|
32
|
-
def __init__(self, parent: tk.Misc, log: Optional[TextIO] = None) -> None:
|
|
33
|
-
"""Store the parent window and the optional diagnostics log.
|
|
34
|
-
|
|
35
|
-
Args:
|
|
36
|
-
parent: The window the wizard window is shown over.
|
|
37
|
-
log: Stream that receives low-level wizard diagnostics.
|
|
38
|
-
"""
|
|
39
|
-
self._parent = parent
|
|
40
|
-
self._log = log
|
|
41
|
-
self._window: Optional[WizardWindow] = None
|
|
42
|
-
|
|
43
|
-
def ask_text(self, question: str, re_ask_reason: Optional[str] = None,
|
|
44
|
-
nullable: bool = False, *, default: Optional[str] = None,
|
|
45
|
-
sensitive: bool = False) -> Optional[str]:
|
|
46
|
-
"""Ask for free text; see WizardUiBridge.ask_text."""
|
|
47
|
-
if sensitive and default is not None:
|
|
48
|
-
raise ValueError('default is not allowed for sensitive input')
|
|
49
|
-
return self._window_obj().ask_text(question, re_ask_reason, nullable,
|
|
50
|
-
default, sensitive)
|
|
51
|
-
|
|
52
|
-
# pylint: disable-next=too-many-arguments
|
|
53
|
-
def ask_int(self, question: str, re_ask_reason: Optional[str] = None, *,
|
|
54
|
-
nullable: bool = False, min_value: Optional[int] = None,
|
|
55
|
-
max_value: Optional[int] = None,
|
|
56
|
-
default: Optional[int] = None) -> Optional[int]:
|
|
57
|
-
"""Ask for an integer within optional bounds; see ask_int."""
|
|
58
|
-
return self._window_obj().ask_int(question, re_ask_reason, nullable,
|
|
59
|
-
min_value, max_value, default)
|
|
60
|
-
|
|
61
|
-
def ask_path(self, question: str, re_ask_reason: Optional[str] = None, *,
|
|
62
|
-
options: Optional[PathAskOptions] = None) -> Optional[Path]:
|
|
63
|
-
"""Ask for a path with a native file or directory picker."""
|
|
64
|
-
path_options = PathAskOptions() if options is None else options
|
|
65
|
-
return self._window_obj().ask_path(question, path_options,
|
|
66
|
-
re_ask_reason)
|
|
67
|
-
|
|
68
|
-
def ask_yes_no(self, question: str, default: bool,
|
|
69
|
-
re_ask_reason: Optional[str] = None) -> bool:
|
|
70
|
-
"""Ask a yes/no question with dedicated yes and no buttons."""
|
|
71
|
-
return self._window_obj().ask_yes_no(question, default, re_ask_reason)
|
|
72
|
-
|
|
73
|
-
def ask_choice(self, question: str, *, choices: Sequence[str],
|
|
74
|
-
default: Optional[str] = None,
|
|
75
|
-
re_ask_reason: Optional[str] = None) -> str:
|
|
76
|
-
"""Ask the user to pick one choice from a single-selection list."""
|
|
77
|
-
return self._window_obj().ask_choice(question, choices, default,
|
|
78
|
-
re_ask_reason)
|
|
79
|
-
|
|
80
|
-
# pylint: disable-next=too-many-arguments
|
|
81
|
-
def ask_multi(self, question: str, *, choices: Sequence[str],
|
|
82
|
-
default: Optional[Sequence[str]] = None, min_select: int = 0,
|
|
83
|
-
max_select: Optional[int] = None,
|
|
84
|
-
re_ask_reason: Optional[str] = None) -> list[str]:
|
|
85
|
-
"""Ask the user to pick several choices from a multi-selection list."""
|
|
86
|
-
return self._window_obj().ask_multi(question, choices, default,
|
|
87
|
-
min_select, max_select,
|
|
88
|
-
re_ask_reason)
|
|
89
|
-
|
|
90
|
-
# pylint: disable-next=too-many-arguments
|
|
91
|
-
def ask_table(self, columns: Sequence[TableColumn],
|
|
92
|
-
cells: list[list[TableCell]], question: str, *,
|
|
93
|
-
re_ask_reason: Optional[str] = None,
|
|
94
|
-
partial_check: Optional[PartialCheck] = None,
|
|
95
|
-
min_rows: Optional[int] = None,
|
|
96
|
-
max_rows: Optional[int] = None) -> list[list[Optional[str]]]:
|
|
97
|
-
"""Ask the user to fill an editable table of the given rows.
|
|
98
|
-
|
|
99
|
-
With both ``min_rows`` and ``max_rows`` given the table has a
|
|
100
|
-
variable number of rows: add-row and remove-row buttons grow the
|
|
101
|
-
table up to ``max_rows`` and shrink it down to ``min_rows``.
|
|
102
|
-
Otherwise the rows given in ``cells`` are fixed and only filled.
|
|
103
|
-
"""
|
|
104
|
-
return self._window_obj().ask_table(columns, cells, question,
|
|
105
|
-
re_ask_reason, partial_check,
|
|
106
|
-
min_rows, max_rows)
|
|
107
|
-
|
|
108
|
-
def ask_form(self, long_question: str, ask_fields: AskFields, *,
|
|
109
|
-
re_ask_reason: Optional[str] = None,
|
|
110
|
-
partial_validator: Optional[PartialFormValidator] = None) \
|
|
111
|
-
-> AnswerFields:
|
|
112
|
-
"""Ask a whole form on one screen; see WizardUiBridge.ask_form."""
|
|
113
|
-
return self._window_obj().ask_form(long_question, ask_fields,
|
|
114
|
-
re_ask_reason, partial_validator)
|
|
115
|
-
|
|
116
|
-
def supports_form_field(self, field: AskField) -> bool:
|
|
117
|
-
"""Report that the Tk form shows every current field type."""
|
|
118
|
-
return handles_field(field)
|
|
119
|
-
|
|
120
|
-
def show(self, message: str) -> None:
|
|
121
|
-
"""Show an informational message to the user."""
|
|
122
|
-
self._window_obj().show(message)
|
|
123
|
-
|
|
124
|
-
def error_file(self) -> TextIO:
|
|
125
|
-
"""Return the stream used for low-level wizard diagnostics."""
|
|
126
|
-
return self._log if self._log is not None else NoTextIO()
|
|
127
|
-
|
|
128
|
-
def close(self) -> None:
|
|
129
|
-
"""Close the wizard window when one was opened."""
|
|
130
|
-
if self._window is not None:
|
|
131
|
-
self._window.close()
|
|
132
|
-
self._window = None
|
|
133
|
-
|
|
134
|
-
def _window_obj(self) -> WizardWindow:
|
|
135
|
-
"""Return the wizard window, creating it on first use."""
|
|
136
|
-
if self._window is None:
|
|
137
|
-
self._window = WizardWindow(self._parent)
|
|
138
|
-
return self._window
|