klab-pytest-toolkit-prompt 0.0.0__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.
- klab_pytest_toolkit_prompt-0.0.0/.gitignore +177 -0
- klab_pytest_toolkit_prompt-0.0.0/PKG-INFO +96 -0
- klab_pytest_toolkit_prompt-0.0.0/README.md +82 -0
- klab_pytest_toolkit_prompt-0.0.0/pyproject.toml +39 -0
- klab_pytest_toolkit_prompt-0.0.0/src/klab_pytest_toolkit_prompt/__init__.py +10 -0
- klab_pytest_toolkit_prompt-0.0.0/src/klab_pytest_toolkit_prompt/core.py +45 -0
- klab_pytest_toolkit_prompt-0.0.0/src/klab_pytest_toolkit_prompt/plugin.py +24 -0
- klab_pytest_toolkit_prompt-0.0.0/src/klab_pytest_toolkit_prompt/ui.py +116 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Created by https://www.toptal.com/developers/gitignore/api/python
|
|
2
|
+
# Edit at https://www.toptal.com/developers/gitignore?templates=python
|
|
3
|
+
|
|
4
|
+
### Custom ###
|
|
5
|
+
pytest.xml
|
|
6
|
+
pytest-coverage.txt
|
|
7
|
+
|
|
8
|
+
### Python ###
|
|
9
|
+
# Byte-compiled / optimized / DLL files
|
|
10
|
+
__pycache__/
|
|
11
|
+
*.py[cod]
|
|
12
|
+
*$py.class
|
|
13
|
+
|
|
14
|
+
# Distribution / packaging
|
|
15
|
+
.Python
|
|
16
|
+
build/
|
|
17
|
+
develop-eggs/
|
|
18
|
+
dist/
|
|
19
|
+
downloads/
|
|
20
|
+
eggs/
|
|
21
|
+
.eggs/
|
|
22
|
+
lib/
|
|
23
|
+
lib64/
|
|
24
|
+
parts/
|
|
25
|
+
sdist/
|
|
26
|
+
var/
|
|
27
|
+
wheels/
|
|
28
|
+
share/python-wheels/
|
|
29
|
+
*.egg-info/
|
|
30
|
+
.installed.cfg
|
|
31
|
+
*.egg
|
|
32
|
+
MANIFEST
|
|
33
|
+
|
|
34
|
+
# PyInstaller
|
|
35
|
+
# Usually these files are written by a python script from a template
|
|
36
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
37
|
+
*.manifest
|
|
38
|
+
*.spec
|
|
39
|
+
|
|
40
|
+
# Installer logs
|
|
41
|
+
pip-log.txt
|
|
42
|
+
pip-delete-this-directory.txt
|
|
43
|
+
|
|
44
|
+
# Unit test / coverage reports
|
|
45
|
+
htmlcov/
|
|
46
|
+
.tox/
|
|
47
|
+
.nox/
|
|
48
|
+
.coverage
|
|
49
|
+
.coverage.*
|
|
50
|
+
.cache
|
|
51
|
+
nosetests.xml
|
|
52
|
+
coverage.xml
|
|
53
|
+
*.cover
|
|
54
|
+
*.py,cover
|
|
55
|
+
.hypothesis/
|
|
56
|
+
.pytest_cache/
|
|
57
|
+
cover/
|
|
58
|
+
|
|
59
|
+
# Translations
|
|
60
|
+
*.mo
|
|
61
|
+
*.pot
|
|
62
|
+
|
|
63
|
+
# Django stuff:
|
|
64
|
+
*.log
|
|
65
|
+
local_settings.py
|
|
66
|
+
db.sqlite3
|
|
67
|
+
db.sqlite3-journal
|
|
68
|
+
|
|
69
|
+
# Flask stuff:
|
|
70
|
+
instance/
|
|
71
|
+
.webassets-cache
|
|
72
|
+
|
|
73
|
+
# Scrapy stuff:
|
|
74
|
+
.scrapy
|
|
75
|
+
|
|
76
|
+
# Sphinx documentation
|
|
77
|
+
docs/_build/
|
|
78
|
+
|
|
79
|
+
# PyBuilder
|
|
80
|
+
.pybuilder/
|
|
81
|
+
target/
|
|
82
|
+
|
|
83
|
+
# Jupyter Notebook
|
|
84
|
+
.ipynb_checkpoints
|
|
85
|
+
|
|
86
|
+
# IPython
|
|
87
|
+
profile_default/
|
|
88
|
+
ipython_config.py
|
|
89
|
+
|
|
90
|
+
# pyenv
|
|
91
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
92
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
93
|
+
# .python-version
|
|
94
|
+
|
|
95
|
+
# pipenv
|
|
96
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
97
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
98
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
99
|
+
# install all needed dependencies.
|
|
100
|
+
#Pipfile.lock
|
|
101
|
+
|
|
102
|
+
# poetry
|
|
103
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
104
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
105
|
+
# commonly ignored for libraries.
|
|
106
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
107
|
+
#poetry.lock
|
|
108
|
+
|
|
109
|
+
# pdm
|
|
110
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
111
|
+
#pdm.lock
|
|
112
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
113
|
+
# in version control.
|
|
114
|
+
# https://pdm.fming.dev/#use-with-ide
|
|
115
|
+
.pdm.toml
|
|
116
|
+
|
|
117
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
118
|
+
__pypackages__/
|
|
119
|
+
|
|
120
|
+
# Celery stuff
|
|
121
|
+
celerybeat-schedule
|
|
122
|
+
celerybeat.pid
|
|
123
|
+
|
|
124
|
+
# SageMath parsed files
|
|
125
|
+
*.sage.py
|
|
126
|
+
|
|
127
|
+
# Environments
|
|
128
|
+
.env
|
|
129
|
+
.venv
|
|
130
|
+
env/
|
|
131
|
+
venv/
|
|
132
|
+
ENV/
|
|
133
|
+
env.bak/
|
|
134
|
+
venv.bak/
|
|
135
|
+
|
|
136
|
+
# Spyder project settings
|
|
137
|
+
.spyderproject
|
|
138
|
+
.spyproject
|
|
139
|
+
|
|
140
|
+
# Rope project settings
|
|
141
|
+
.ropeproject
|
|
142
|
+
|
|
143
|
+
# mkdocs documentation
|
|
144
|
+
/site
|
|
145
|
+
|
|
146
|
+
# mypy
|
|
147
|
+
.mypy_cache/
|
|
148
|
+
.dmypy.json
|
|
149
|
+
dmypy.json
|
|
150
|
+
|
|
151
|
+
# Pyre type checker
|
|
152
|
+
.pyre/
|
|
153
|
+
|
|
154
|
+
# pytype static type analyzer
|
|
155
|
+
.pytype/
|
|
156
|
+
|
|
157
|
+
# Cython debug symbols
|
|
158
|
+
cython_debug/
|
|
159
|
+
|
|
160
|
+
# PyCharm
|
|
161
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
162
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
163
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
164
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
165
|
+
#.idea/
|
|
166
|
+
|
|
167
|
+
### Python Patch ###
|
|
168
|
+
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
|
169
|
+
poetry.toml
|
|
170
|
+
|
|
171
|
+
# ruff
|
|
172
|
+
.ruff_cache/
|
|
173
|
+
|
|
174
|
+
# LSP config files
|
|
175
|
+
pyrightconfig.json
|
|
176
|
+
|
|
177
|
+
# End of https://www.toptal.com/developers/gitignore/api/python
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: klab-pytest-toolkit-prompt
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Pytest prompt fixtures for the Klab Pytest Toolkit
|
|
5
|
+
Author-email: Burak Kizilkaya <burak.kizilkaya@outlook.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Requires-Python: <4,>=3.11
|
|
12
|
+
Requires-Dist: pytest>=8.3.5
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# Klab Pytest Toolkit - Prompt
|
|
16
|
+
|
|
17
|
+
Custom pytest fixtures for interactive user prompts during test execution using tkinter UI dialogs.
|
|
18
|
+
The goal is to allow testers to interact with the test process, providing confirmations or displaying important information.
|
|
19
|
+
|
|
20
|
+
At the moment the package provides the following fixtures:
|
|
21
|
+
|
|
22
|
+
- `ui_prompt_factory`: Factory for creating multiple prompt instance. With the factory you can create the prompt fixture.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install klab-pytest-toolkit-prompt
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
### UI Prompt
|
|
33
|
+
|
|
34
|
+
**Create the fixture**
|
|
35
|
+
|
|
36
|
+
The factory class `PromptFactory` can be used to create multiple prompt instances.
|
|
37
|
+
This is already provided as a pytest fixture `prompt_factory`.
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
|
|
41
|
+
@pytest.fixture
|
|
42
|
+
def ui_prompt(prompt_factory) -> PromptInterface:
|
|
43
|
+
"""Fixture to provide a UI prompt interface for user interaction during tests."""
|
|
44
|
+
return prompt_factory.create_prompt(prompt_type=PromptFactory.PromptType.UI_PROMPT)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Functions**
|
|
48
|
+
|
|
49
|
+
The following functions are available on the `PromptInterface` instance:
|
|
50
|
+
|
|
51
|
+
**Show Information Dialog**
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
def test_with_info(ui_prompt):
|
|
55
|
+
"""Display information to the user."""
|
|
56
|
+
ui_prompt.show_info("Test is about to perform a critical operation")
|
|
57
|
+
|
|
58
|
+
# Continue with test
|
|
59
|
+
perform_operation()
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Get User Confirmation**
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
def test_with_confirmation(ui_prompt):
|
|
66
|
+
"""Get user confirmation before proceeding."""
|
|
67
|
+
if ui_prompt.confirm_action("Continue with destructive test?"):
|
|
68
|
+
# User clicked Yes
|
|
69
|
+
perform_destructive_operation()
|
|
70
|
+
else:
|
|
71
|
+
# User clicked No
|
|
72
|
+
pytest.skip("User cancelled the test")
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**With Timeout (Auto-close)**
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
def test_with_timeout(ui_prompt):
|
|
79
|
+
"""Show dialog that auto-closes after timeout."""
|
|
80
|
+
# Dialog closes automatically after 5 seconds
|
|
81
|
+
ui_prompt.show_info("This will auto-close in 5 seconds", timeout=5)
|
|
82
|
+
|
|
83
|
+
# Confirmation dialog with timeout (returns False if timeout expires)
|
|
84
|
+
result = ui_prompt.confirm_action(
|
|
85
|
+
"Click within 10 seconds",
|
|
86
|
+
timeout=10
|
|
87
|
+
)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Examples
|
|
91
|
+
|
|
92
|
+
See the `tests` directory for example test cases demonstrating the usage of the prompt fixtures.
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
MIT
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Klab Pytest Toolkit - Prompt
|
|
2
|
+
|
|
3
|
+
Custom pytest fixtures for interactive user prompts during test execution using tkinter UI dialogs.
|
|
4
|
+
The goal is to allow testers to interact with the test process, providing confirmations or displaying important information.
|
|
5
|
+
|
|
6
|
+
At the moment the package provides the following fixtures:
|
|
7
|
+
|
|
8
|
+
- `ui_prompt_factory`: Factory for creating multiple prompt instance. With the factory you can create the prompt fixture.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install klab-pytest-toolkit-prompt
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
### UI Prompt
|
|
19
|
+
|
|
20
|
+
**Create the fixture**
|
|
21
|
+
|
|
22
|
+
The factory class `PromptFactory` can be used to create multiple prompt instances.
|
|
23
|
+
This is already provided as a pytest fixture `prompt_factory`.
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
|
|
27
|
+
@pytest.fixture
|
|
28
|
+
def ui_prompt(prompt_factory) -> PromptInterface:
|
|
29
|
+
"""Fixture to provide a UI prompt interface for user interaction during tests."""
|
|
30
|
+
return prompt_factory.create_prompt(prompt_type=PromptFactory.PromptType.UI_PROMPT)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Functions**
|
|
34
|
+
|
|
35
|
+
The following functions are available on the `PromptInterface` instance:
|
|
36
|
+
|
|
37
|
+
**Show Information Dialog**
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
def test_with_info(ui_prompt):
|
|
41
|
+
"""Display information to the user."""
|
|
42
|
+
ui_prompt.show_info("Test is about to perform a critical operation")
|
|
43
|
+
|
|
44
|
+
# Continue with test
|
|
45
|
+
perform_operation()
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Get User Confirmation**
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
def test_with_confirmation(ui_prompt):
|
|
52
|
+
"""Get user confirmation before proceeding."""
|
|
53
|
+
if ui_prompt.confirm_action("Continue with destructive test?"):
|
|
54
|
+
# User clicked Yes
|
|
55
|
+
perform_destructive_operation()
|
|
56
|
+
else:
|
|
57
|
+
# User clicked No
|
|
58
|
+
pytest.skip("User cancelled the test")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**With Timeout (Auto-close)**
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
def test_with_timeout(ui_prompt):
|
|
65
|
+
"""Show dialog that auto-closes after timeout."""
|
|
66
|
+
# Dialog closes automatically after 5 seconds
|
|
67
|
+
ui_prompt.show_info("This will auto-close in 5 seconds", timeout=5)
|
|
68
|
+
|
|
69
|
+
# Confirmation dialog with timeout (returns False if timeout expires)
|
|
70
|
+
result = ui_prompt.confirm_action(
|
|
71
|
+
"Click within 10 seconds",
|
|
72
|
+
timeout=10
|
|
73
|
+
)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Examples
|
|
77
|
+
|
|
78
|
+
See the `tests` directory for example test cases demonstrating the usage of the prompt fixtures.
|
|
79
|
+
|
|
80
|
+
## License
|
|
81
|
+
|
|
82
|
+
MIT
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "klab-pytest-toolkit-prompt"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Pytest prompt fixtures for the Klab Pytest Toolkit"
|
|
9
|
+
authors = [{ name = "Burak Kizilkaya", email = "burak.kizilkaya@outlook.com" }]
|
|
10
|
+
requires-python = ">=3.11,<4"
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
license = "MIT"
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
]
|
|
19
|
+
dependencies = [
|
|
20
|
+
"pytest>=8.3.5",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.entry-points.pytest11]
|
|
24
|
+
klab_pytest_toolkit_prompt = "klab_pytest_toolkit_prompt.plugin"
|
|
25
|
+
|
|
26
|
+
[tool.hatch.version]
|
|
27
|
+
path = "src/klab_pytest_toolkit_prompt/__init__.py"
|
|
28
|
+
|
|
29
|
+
[tool.hatch.build.targets.sdist]
|
|
30
|
+
exclude = [
|
|
31
|
+
"tests",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[tool.pytest.ini_options]
|
|
35
|
+
asyncio_mode = "auto"
|
|
36
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
37
|
+
pythonpath = [
|
|
38
|
+
"."
|
|
39
|
+
]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class PromptInterface(abc.ABC):
|
|
6
|
+
"""Abstract base class for prompt interfaces."""
|
|
7
|
+
|
|
8
|
+
@abc.abstractmethod
|
|
9
|
+
def show_info(self, message: str, timeout: Optional[int] = None) -> None:
|
|
10
|
+
"""Display an informational message to the user."""
|
|
11
|
+
raise NotImplementedError
|
|
12
|
+
|
|
13
|
+
def confirm_action(self, message: str, timeout: Optional[int] = None) -> Optional[bool]:
|
|
14
|
+
"""Prompt the user to confirm an action."""
|
|
15
|
+
raise NotImplementedError
|
|
16
|
+
|
|
17
|
+
def get_user_input(self, prompt: str, timeout: Optional[int] = None) -> Optional[str]:
|
|
18
|
+
"""Prompt the user for input."""
|
|
19
|
+
raise NotImplementedError
|
|
20
|
+
|
|
21
|
+
def __enter__(self):
|
|
22
|
+
"""Enter the runtime context related to this object."""
|
|
23
|
+
return self
|
|
24
|
+
|
|
25
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
26
|
+
"""Exit the runtime context related to this object."""
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class PromptFactory:
|
|
31
|
+
"""Factory for creating prompt interface instances."""
|
|
32
|
+
|
|
33
|
+
class PromptType:
|
|
34
|
+
UI_PROMPT = "ui_prompt"
|
|
35
|
+
# Future prompt types can be added here
|
|
36
|
+
|
|
37
|
+
@staticmethod
|
|
38
|
+
def create_prompt(prompt_type: str = PromptType.UI_PROMPT) -> PromptInterface:
|
|
39
|
+
"""Create a prompt interface instance based on the specified type."""
|
|
40
|
+
if prompt_type == PromptFactory.PromptType.UI_PROMPT:
|
|
41
|
+
from klab_pytest_toolkit_prompt.ui import UiPrompt
|
|
42
|
+
|
|
43
|
+
return UiPrompt()
|
|
44
|
+
else:
|
|
45
|
+
raise ValueError(f"Unsupported prompt type: {prompt_type}")
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Pytest plugin to register prompt fixtures."""
|
|
2
|
+
|
|
3
|
+
from klab_pytest_toolkit_prompt.core import PromptFactory
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def pytest_configure(config):
|
|
8
|
+
"""Register custom markers and configure prompt fixtures."""
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def pytest_addoption(parser):
|
|
13
|
+
"""Add command line and ini file options."""
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.fixture
|
|
18
|
+
def prompt_factory() -> PromptFactory:
|
|
19
|
+
"""Factory fixture to create prompt interface instances.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
PromptFactory: An instance of the PromptFactory
|
|
23
|
+
"""
|
|
24
|
+
return PromptFactory()
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
from tkinter import messagebox, simpledialog
|
|
3
|
+
from typing import Any, Callable, Optional, TypeVar
|
|
4
|
+
|
|
5
|
+
from klab_pytest_toolkit_prompt.core import PromptInterface
|
|
6
|
+
|
|
7
|
+
T = TypeVar("T")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class UiPrompt(PromptInterface):
|
|
11
|
+
"""Concrete implementation of PromptInterface for UI prompts.
|
|
12
|
+
|
|
13
|
+
The implementation will be done with tkinter, because it is included in the standard library.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def _run_dialog(
|
|
17
|
+
self,
|
|
18
|
+
dialog_fn: Callable[[tk.Tk], T],
|
|
19
|
+
timeout: Optional[int] = None,
|
|
20
|
+
default: Optional[T] = None,
|
|
21
|
+
) -> Optional[T]:
|
|
22
|
+
"""Run a dialog with optional timeout handling.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
dialog_fn: Function that takes root window and returns dialog result
|
|
26
|
+
timeout: Optional timeout in seconds
|
|
27
|
+
default: Default value to return on timeout or error
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
The dialog result or default value
|
|
31
|
+
"""
|
|
32
|
+
result: Any = default
|
|
33
|
+
timeout_expired = False
|
|
34
|
+
|
|
35
|
+
def show():
|
|
36
|
+
nonlocal result, timeout_expired
|
|
37
|
+
root = tk.Tk()
|
|
38
|
+
root.withdraw()
|
|
39
|
+
root.update_idletasks()
|
|
40
|
+
|
|
41
|
+
if timeout:
|
|
42
|
+
|
|
43
|
+
def auto_close():
|
|
44
|
+
nonlocal timeout_expired
|
|
45
|
+
timeout_expired = True
|
|
46
|
+
self._safe_destroy(root)
|
|
47
|
+
|
|
48
|
+
root.after(timeout * 1000, auto_close)
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
dialog_result = dialog_fn(root)
|
|
52
|
+
if not timeout_expired:
|
|
53
|
+
result = dialog_result
|
|
54
|
+
except tk.TclError:
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
self._safe_destroy(root)
|
|
58
|
+
|
|
59
|
+
show()
|
|
60
|
+
return result
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
def _safe_destroy(root: tk.Tk) -> None:
|
|
64
|
+
"""Safely quit and destroy a tkinter root window."""
|
|
65
|
+
try:
|
|
66
|
+
root.quit()
|
|
67
|
+
root.destroy()
|
|
68
|
+
except tk.TclError:
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
def show_info(self, message: str, timeout: Optional[int] = None) -> None:
|
|
72
|
+
"""Display an informational message to the user.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
message: The message to display
|
|
76
|
+
timeout: Optional timeout in seconds. If provided, the dialog will
|
|
77
|
+
automatically close after this time.
|
|
78
|
+
"""
|
|
79
|
+
self._run_dialog(
|
|
80
|
+
lambda root: messagebox.showinfo("Information", message, parent=root),
|
|
81
|
+
timeout=timeout,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def confirm_action(self, message: str, timeout: Optional[int] = None) -> Optional[bool]:
|
|
85
|
+
"""Prompt the user to confirm an action.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
message: The confirmation message to display
|
|
89
|
+
timeout: Optional timeout in seconds. If provided and expires,
|
|
90
|
+
returns False (no confirmation).
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
bool: True if user confirmed (clicked Yes/OK), False otherwise
|
|
94
|
+
"""
|
|
95
|
+
return self._run_dialog(
|
|
96
|
+
lambda root: messagebox.askyesno("Confirm Action", message, parent=root),
|
|
97
|
+
timeout=timeout,
|
|
98
|
+
default=False,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def get_user_input(self, prompt: str, timeout: Optional[int] = None) -> Optional[str]:
|
|
102
|
+
"""Prompt the user for input.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
prompt: The prompt message to display
|
|
106
|
+
timeout: Optional timeout in seconds. If provided and expires,
|
|
107
|
+
returns None.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Optional[str]: The user input, or None if cancelled or timed out
|
|
111
|
+
"""
|
|
112
|
+
return self._run_dialog(
|
|
113
|
+
lambda root: simpledialog.askstring("Input Required", prompt, parent=root),
|
|
114
|
+
timeout=timeout,
|
|
115
|
+
default=None,
|
|
116
|
+
)
|