mininterface 0.7.2__tar.gz → 0.7.3__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.
Files changed (39) hide show
  1. {mininterface-0.7.2 → mininterface-0.7.3}/PKG-INFO +8 -2
  2. {mininterface-0.7.2 → mininterface-0.7.3}/README.md +4 -0
  3. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/cli_parser.py +33 -22
  4. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/textual_interface/textual_adaptor.py +5 -0
  5. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/textual_interface/textual_app.py +9 -0
  6. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/textual_interface/textual_button_app.py +3 -0
  7. mininterface-0.7.3/mininterface/textual_interface/textual_facet.py +64 -0
  8. mininterface-0.7.3/mininterface/tk_interface/external_fix.py +74 -0
  9. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/tk_interface/tk_facet.py +7 -4
  10. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/tk_interface/utils.py +1 -0
  11. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/types.py +3 -10
  12. {mininterface-0.7.2 → mininterface-0.7.3}/pyproject.toml +4 -2
  13. mininterface-0.7.2/mininterface/textual_interface/textual_facet.py +0 -31
  14. {mininterface-0.7.2 → mininterface-0.7.3}/LICENSE +0 -0
  15. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/ValidationFail.py +0 -0
  16. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/__init__.py +0 -0
  17. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/__main__.py +0 -0
  18. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/auxiliary.py +0 -0
  19. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/exceptions.py +0 -0
  20. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/experimental.py +0 -0
  21. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/facet.py +0 -0
  22. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/form_dict.py +0 -0
  23. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/interfaces.py +0 -0
  24. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/mininterface.py +0 -0
  25. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/redirectable.py +0 -0
  26. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/showcase.py +0 -0
  27. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/start.py +0 -0
  28. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/subcommands.py +0 -0
  29. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/tag.py +0 -0
  30. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/tag_factory.py +0 -0
  31. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/text_interface.py +0 -0
  32. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/textual_interface/__init__.py +0 -0
  33. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/textual_interface/widgets.py +0 -0
  34. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/tk_interface/__init__.py +0 -0
  35. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/tk_interface/date_entry.py +0 -0
  36. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/tk_interface/redirect_text_tkinter.py +0 -0
  37. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/tk_interface/tk_window.py +0 -0
  38. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/type_stubs.py +0 -0
  39. {mininterface-0.7.2 → mininterface-0.7.3}/mininterface/validators.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: mininterface
3
- Version: 0.7.2
3
+ Version: 0.7.3
4
4
  Summary: A minimal access to GUI, TUI, CLI and config
5
5
  Home-page: https://github.com/CZ-NIC/mininterface
6
6
  License: GPL-3.0-or-later
@@ -15,6 +15,8 @@ Classifier: Programming Language :: Python :: 3.12
15
15
  Classifier: Programming Language :: Python :: 3.13
16
16
  Provides-Extra: all
17
17
  Provides-Extra: gui
18
+ Provides-Extra: img
19
+ Provides-Extra: tui
18
20
  Provides-Extra: web
19
21
  Requires-Dist: autocombobox (==1.4.2)
20
22
  Requires-Dist: humanize
@@ -143,6 +145,10 @@ pip install --no-dependencies mininterface
143
145
  pip install tyro typing_extensions pyyaml
144
146
  ```
145
147
 
148
+ ## MacOS GUI
149
+
150
+ If the GUI does not work on MacOS, you might need to launch: `brew install python-tk`
151
+
146
152
  # Docs
147
153
  See the docs overview at [https://cz-nic.github.io/mininterface/](https://cz-nic.github.io/mininterface/Overview/).
148
154
 
@@ -114,6 +114,10 @@ pip install --no-dependencies mininterface
114
114
  pip install tyro typing_extensions pyyaml
115
115
  ```
116
116
 
117
+ ## MacOS GUI
118
+
119
+ If the GUI does not work on MacOS, you might need to launch: `brew install python-tk`
120
+
117
121
  # Docs
118
122
  See the docs overview at [https://cz-nic.github.io/mininterface/](https://cz-nic.github.io/mininterface/Overview/).
119
123
 
@@ -150,7 +150,7 @@ def run_tyro_parser(env_or_list: Type[EnvClass] | list[Type[EnvClass]],
150
150
  if ask_for_missing and getattr(e, "code", None) == 2 and eavesdrop:
151
151
  # Some required arguments are missing. Determine which.
152
152
  wf = {}
153
- for arg in eavesdrop.partition(":")[2].strip().split(", "):
153
+ for arg in _fetch_eavesdrop_args():
154
154
  treat_missing(type_form, kwargs, parser, wf, arg)
155
155
 
156
156
  # Second attempt to parse CLI
@@ -195,7 +195,6 @@ def treat_missing(env_class, kwargs: dict, parser: ArgumentParser, wf: dict, arg
195
195
  # However, the UI then is not able to use ex. the number filtering capabilities.
196
196
  # Putting there None is not a good idea as dataclass_to_tagdict fails if None is not allowed by the annotation.
197
197
  tag = wf[field_name] = tag_factory(MissingTagValue(),
198
- # tag = wf[field_name] = tag_factory(MISSING,
199
198
  argument.help.replace("(required)", ""),
200
199
  validation=not_empty,
201
200
  _src_class=env_class,
@@ -205,9 +204,17 @@ def treat_missing(env_class, kwargs: dict, parser: ArgumentParser, wf: dict, arg
205
204
  # A None would be enough because Mininterface will ask for the missing values
206
205
  # promply, however, Pydantic model would fail.
207
206
  # As it serves only for tyro parsing and the field is marked wrong, the made up value is never used or seen.
208
- if "default" not in kwargs:
209
- kwargs["default"] = SimpleNamespace()
210
- setattr(kwargs["default"], field_name, tag._make_default_value())
207
+ set_default(kwargs, field_name, tag._make_default_value())
208
+
209
+
210
+ def _fetch_eavesdrop_args():
211
+ return eavesdrop.partition(":")[2].strip().split(", ")
212
+
213
+
214
+ def set_default(kwargs, field_name, val):
215
+ if "default" not in kwargs:
216
+ kwargs["default"] = SimpleNamespace()
217
+ setattr(kwargs["default"], field_name, val)
211
218
 
212
219
 
213
220
  def _parse_cli(env_or_list: Type[EnvClass] | list[Type[EnvClass]],
@@ -228,15 +235,20 @@ def _parse_cli(env_or_list: Type[EnvClass] | list[Type[EnvClass]],
228
235
  Returns:
229
236
  Configuration namespace.
230
237
  """
238
+ if isinstance(env_or_list, list):
239
+ subcommands, env = env_or_list, None
240
+ else:
241
+ subcommands, env = None, env_or_list
242
+
231
243
  # Load config file
232
- if config_file and isinstance(env_or_list, list):
233
- # NOTE. Reading config files when using subcommands is not implemented.
244
+ if config_file and subcommands:
245
+ # Reading config files when using subcommands is not implemented.
234
246
  static = {}
235
247
  kwargs["default"] = None
236
248
  warnings.warn(f"Config file {config_file} is ignored because subcommands are used."
237
- "It is not easy to set who this should work. "
238
- "Describe the developer your usecase so that they might implement this.")
239
- if "default" not in kwargs and not isinstance(env_or_list, list):
249
+ " It is not easy to set how this should work."
250
+ " Describe the developer your usecase so that they might implement this.")
251
+ if "default" not in kwargs and not subcommands:
240
252
  # Undocumented feature. User put a namespace into kwargs["default"]
241
253
  # that already serves for defaults. We do not fetch defaults yet from a config file.
242
254
  disk = {}
@@ -244,29 +256,28 @@ def _parse_cli(env_or_list: Type[EnvClass] | list[Type[EnvClass]],
244
256
  disk = yaml.safe_load(config_file.read_text()) or {} # empty file is ok
245
257
  # Nested dataclasses have to be properly initialized. YAML gave them as dicts only.
246
258
  for key in (key for key, val in disk.items() if isinstance(val, dict)):
247
- disk[key] = env_or_list.__annotations__[key](**disk[key])
259
+ disk[key] = env.__annotations__[key](**disk[key])
248
260
 
249
261
  # Fill default fields
250
- if pydantic and issubclass(env_or_list, BaseModel):
262
+ if pydantic and issubclass(env, BaseModel):
251
263
  # Unfortunately, pydantic needs to fill the default with the actual values,
252
264
  # the default value takes the precedence over the hard coded one, even if missing.
253
- static = {key: env_or_list.model_fields.get(key).default
254
- for ann in yield_annotations(env_or_list) for key in ann if not key.startswith("__") and not key in disk}
255
- # static = {key: env_or_list.model_fields.get(key).default
256
- # for key, _ in iterate_attributes(env_or_list) if not key in disk}
257
- elif attr and attr.has(env_or_list):
265
+ static = {key: env.model_fields.get(key).default
266
+ for ann in yield_annotations(env) for key in ann if not key.startswith("__") and not key in disk}
267
+ # static = {key: env_.model_fields.get(key).default
268
+ # for key, _ in iterate_attributes(env_) if not key in disk}
269
+ elif attr and attr.has(env):
258
270
  # Unfortunately, attrs needs to fill the default with the actual values,
259
271
  # the default value takes the precedence over the hard coded one, even if missing.
260
272
  # NOTE Might not work for inherited models.
261
273
  static = {key: field.default
262
- for key, field in attr.fields_dict(env_or_list).items() if not key.startswith("__") and not key in disk}
274
+ for key, field in attr.fields_dict(env).items() if not key.startswith("__") and not key in disk}
263
275
  else:
264
276
  # To ensure the configuration file does not need to contain all keys, we have to fill in the missing ones.
265
277
  # Otherwise, tyro will spawn warnings about missing fields.
266
278
  static = {key: val
267
- for key, val in yield_defaults(env_or_list) if not key.startswith("__") and not key in disk}
268
- kwargs["default"] = SimpleNamespace(**(disk | static))
279
+ for key, val in yield_defaults(env) if not key.startswith("__") and not key in disk}
280
+ kwargs["default"] = SimpleNamespace(**(static | disk))
269
281
 
270
282
  # Load configuration from CLI
271
- env, wrong_fields = run_tyro_parser(env_or_list, kwargs, add_verbosity, ask_for_missing, args)
272
- return env, wrong_fields
283
+ return run_tyro_parser(subcommands or env, kwargs, add_verbosity, ask_for_missing, args)
@@ -28,6 +28,8 @@ class TextualAdaptor(BackendAdaptor):
28
28
  def __init__(self, interface: "TextualInterface"):
29
29
  self.interface = interface
30
30
  self.facet = interface.facet = TextualFacet(self, interface.env)
31
+ self.app: TextualApp | None = None
32
+ self.layout_elements = []
31
33
 
32
34
  @staticmethod
33
35
  def widgetize(tag: Tag) -> Widget | Changeable:
@@ -73,6 +75,9 @@ class TextualAdaptor(BackendAdaptor):
73
75
 
74
76
  def run_dialog(self, form: TagDict, title: str = "", submit: bool | str = True) -> TagDict:
75
77
  super().run_dialog(form, title, submit)
78
+ # Unfortunately, there seems to be no way to reuse the app.
79
+ # Which blocks using multiple form external .form() calls from the web interface.
80
+ # Textual cannot run in a thread, it seems it cannot run in another process, self.suspend() is of no use.
76
81
  self.app = app = TextualApp(self, submit)
77
82
  if title:
78
83
  app.title = title
@@ -26,6 +26,14 @@ class TextualApp(App[bool | None]):
26
26
  # ("down", "go_up", "Go down"),
27
27
  # ]
28
28
 
29
+ DEFAULT_CSS = """
30
+ ImageViewer{
31
+
32
+ height: 20;
33
+ }
34
+ """
35
+ """ Limit layout image size """
36
+
29
37
  def __init__(self, adaptor: "TextualAdaptor", submit: str | bool = True):
30
38
  super().__init__()
31
39
  self.title = adaptor.facet._title
@@ -52,6 +60,7 @@ class TextualApp(App[bool | None]):
52
60
  yield Label(text, id="buffered_text")
53
61
  focus_set = False
54
62
  with VerticalScroll():
63
+ yield from self.adaptor.layout_elements
55
64
  for i, fieldt in enumerate(self.widgets):
56
65
  if isinstance(fieldt, Input):
57
66
  yield Label(fieldt.placeholder)
@@ -2,6 +2,7 @@ from dataclasses import dataclass
2
2
  from typing import TYPE_CHECKING, Any
3
3
 
4
4
  from textual.app import App, ComposeResult
5
+ from textual.containers import VerticalScroll
5
6
  from textual.widgets import Button, Footer, Label
6
7
 
7
8
  from ..exceptions import Cancelled
@@ -60,6 +61,7 @@ class TextualButtonApp(App):
60
61
  self.focused_i: int = 0
61
62
  self.values = {}
62
63
  self.interface = interface
64
+ self.adaptor = self.interface.adaptor
63
65
 
64
66
  def yes_no(self, text: str, focus_no=True) -> DummyWrapper:
65
67
  return self.buttons(text, [("Yes", True), ("No", False)], int(focus_no))
@@ -78,6 +80,7 @@ class TextualButtonApp(App):
78
80
  yield Footer()
79
81
  if text := self.interface._redirected.join():
80
82
  yield Label(text, id="buffered_text")
83
+ yield from self.adaptor.layout_elements
81
84
  yield Label(self.text, id="question")
82
85
 
83
86
  self.values.clear()
@@ -0,0 +1,64 @@
1
+ from datetime import datetime
2
+ from typing import TYPE_CHECKING
3
+ from warnings import warn
4
+ from pathlib import Path
5
+
6
+ from textual.widgets import (Checkbox, Footer, Header, Input, Label,
7
+ RadioButton, Static)
8
+
9
+ from humanize import naturalsize
10
+
11
+ from ..exceptions import DependencyRequired
12
+ from ..facet import Facet, Image, LayoutElement
13
+ if TYPE_CHECKING:
14
+ from .textual_adaptor import TextualAdaptor
15
+
16
+
17
+ class TextualFacet(Facet):
18
+ adaptor: "TextualAdaptor"
19
+
20
+ def __init__(self, *args, **kwargs):
21
+ super().__init__(*args, **kwargs)
22
+ # Since TextualApp turns off, we need to have its values stored somewhere
23
+ self._title = ""
24
+
25
+ # NOTE: multiline title will not show up
26
+ def set_title(self, title: str):
27
+ self._title = title
28
+ try:
29
+ self.adaptor.app.title = title
30
+ except:
31
+ # NOTE: When you receive Facet in Command.init, the app does not exist yet
32
+ warn("Setting textual title not implemented well.")
33
+
34
+ def _layout(self, elements: list[LayoutElement]):
35
+ append = self.adaptor.layout_elements.append
36
+ try:
37
+ from PIL import Image as ImagePIL
38
+ from textual_imageview.viewer import ImageViewer
39
+ PIL = True
40
+ except:
41
+ PIL = False
42
+
43
+ for el in elements:
44
+ match el:
45
+ case Image():
46
+ if not PIL:
47
+ raise DependencyRequired("img")
48
+ append(ImageViewer(ImagePIL.open(el.src)))
49
+ case Path():
50
+ size = naturalsize(el.stat().st_size)
51
+ mtime = datetime.fromtimestamp(el.stat().st_mtime).strftime("%Y-%m-%d %H:%M:%S")
52
+ append(Label(f"{el} / {size} / {mtime}"))
53
+ case str():
54
+ append(Label(el))
55
+ case _:
56
+ append(Label("Error in the layout: Unknown {el}"))
57
+
58
+ def submit(self, *args, **kwargs):
59
+ super().submit(*args, **kwargs)
60
+ try:
61
+ self.adaptor.app.action_confirm()
62
+ except:
63
+ # NOTE: When you receive Facet in Command.init, the app does not exist yet
64
+ warn("Setting textual title not implemented well.")
@@ -0,0 +1,74 @@
1
+ # The purpose of the file is to put the descriptions to the bottom of the widgets
2
+ # as it was in the former version of the tkinter_form and to limit their width.
3
+ from tkinter import ttk
4
+
5
+ from tkinter_form import Form, Value, FieldForm
6
+
7
+ orig = Form._Form__create_widgets
8
+
9
+
10
+ def __create_widgets_monkeypatched(
11
+ self, form_dict: dict, name_config: str, button_command: callable
12
+ ) -> None:
13
+ """
14
+ Create form widgets
15
+
16
+ Args:
17
+ form_dict (dict): form dict base
18
+ name_config (str): name_config
19
+ button (bool): button_config
20
+ """
21
+
22
+ index = 0
23
+ for _, (name_key, value) in enumerate(form_dict.items()):
24
+ index += 1
25
+ description = None
26
+ if isinstance(value, Value):
27
+ value, description = value.val, value.description
28
+
29
+ self.rowconfigure(index, weight=1)
30
+
31
+ if isinstance(value, dict):
32
+ widget = Form(self, name_key, value)
33
+ widget.grid(row=index, column=0, columnspan=3, sticky="nesw")
34
+
35
+ self.fields[name_key] = widget
36
+ last_index = index
37
+ continue
38
+
39
+ variable = self._Form__type_vars[type(value)]()
40
+ widget = self._Form__type_widgets[type(value)](self)
41
+
42
+ self.columnconfigure(1, weight=1)
43
+ widget.grid(row=index, column=1, sticky="nesw", padx=2, pady=2)
44
+ label = ttk.Label(self, text=name_key)
45
+ self.columnconfigure(0, weight=1)
46
+ label.grid(row=index, column=0, sticky="nes", padx=2, pady=2)
47
+
48
+ # Add a further description to the row below the widget
49
+ description_label = None
50
+ if not description is None:
51
+ index += 1
52
+ description_label = ttk.Label(self, text=description, wraplength=1000)
53
+ description_label.grid(row=index, column=1, columnspan=2, sticky="nesw", padx=2, pady=2)
54
+
55
+ self.fields[name_key] = FieldForm(
56
+ master=self,
57
+ label=label,
58
+ widget=widget,
59
+ variable=variable,
60
+ value=value,
61
+ description=description_label,
62
+ )
63
+
64
+ last_index = index
65
+
66
+ if button_command:
67
+ self._Form__command = button_command
68
+ self.button = ttk.Button(
69
+ self, text=name_config, command=self._Form__command_button
70
+ )
71
+ self.button.grid(row=last_index + 1, column=0, columnspan=3, sticky="nesw")
72
+
73
+
74
+ Form._Form__create_widgets = __create_widgets_monkeypatched
@@ -36,10 +36,13 @@ class TkFacet(Facet):
36
36
  raise DependencyRequired("img")
37
37
  filename = el.src
38
38
  img = ImagePIL.open(filename)
39
- img = img.resize((250, 250))
40
- img = ImageTk.PhotoImage(img)
41
- panel = Label(self.adaptor.frame, image=img)
42
- panel.image = img
39
+ max_width, max_height = 250, 250
40
+ w_o, h_o = img.size
41
+ scale = min(max_width / w_o, max_height / h_o)
42
+ img = img.resize((int(w_o * scale), int(h_o * scale)), ImagePIL.LANCZOS)
43
+ img_p = ImageTk.PhotoImage(img)
44
+ panel = Label(self.adaptor.frame, image=img_p)
45
+ panel.image = img_p
43
46
  panel.pack()
44
47
  case Path():
45
48
  size = naturalsize(el.stat().st_size)
@@ -14,6 +14,7 @@ from ..form_dict import TagDict
14
14
  from ..tag import Tag
15
15
  from ..types import DatetimeTag, PathTag
16
16
  from .date_entry import DateEntryFrame
17
+ from .external_fix import __create_widgets_monkeypatched
17
18
 
18
19
  if TYPE_CHECKING:
19
20
  from tk_window import TkWindow
@@ -159,10 +159,7 @@ class PathTag(Tag):
159
159
  @dataclass(repr=False)
160
160
  class DatetimeTag(Tag):
161
161
  """
162
- !!! warning
163
- Experimental. Still in development.
164
-
165
- Datetime is supported.
162
+ Datetime, date and time types are supported.
166
163
 
167
164
  ```python3
168
165
  from datetime import datetime
@@ -214,13 +211,12 @@ class DatetimeTag(Tag):
214
211
  # ![Time only](asset/datetime_time.avif)
215
212
 
216
213
  # NOTE: It would be nice we might put any date format to be parsed.
217
- # NOTE: The parameters are still ignored.
218
214
 
219
215
  date: bool = False
220
- """ The date part is active """
216
+ """ The date part is active. True for datetime and date. """
221
217
 
222
218
  time: bool = False
223
- """ The time part is active """
219
+ """ The time part is active. True for datetime and time. """
224
220
 
225
221
  full_precision: bool = False
226
222
  """ Include full time precison, seconds, microseconds. """
@@ -230,9 +226,6 @@ class DatetimeTag(Tag):
230
226
  if self.annotation:
231
227
  self.date = issubclass(self.annotation, date)
232
228
  self.time = issubclass(self.annotation, time) or issubclass(self.annotation, datetime)
233
- # NOTE: remove
234
- # if not self.time and self.full_precision:
235
- # self.full_precision = False
236
229
 
237
230
  def _make_default_value(self):
238
231
  return datetime.now()
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "mininterface"
7
- version = "0.7.2"
7
+ version = "0.7.3"
8
8
  description = "A minimal access to GUI, TUI, CLI and config"
9
9
  authors = ["Edvard Rejthar <edvard.rejthar@nic.cz>"]
10
10
  license = "GPL-3.0-or-later"
@@ -27,8 +27,10 @@ tkscrollableframe = "*"
27
27
 
28
28
  [tool.poetry.extras]
29
29
  web = ["textual-serve"]
30
+ img = ["pillow", "textual_imageview"]
31
+ tui = ["textual_imageview"]
30
32
  gui = ["pillow", "tkcalendar"]
31
- all = ["textual-serve", "pillow", "tkcalendar"]
33
+ all = ["textual-serve", "pillow", "tkcalendar", "textual_imageview"]
32
34
 
33
35
  [tool.poetry.scripts]
34
36
  mininterface = "mininterface.__main__:main"
@@ -1,31 +0,0 @@
1
- from typing import TYPE_CHECKING
2
- from warnings import warn
3
- from ..facet import Facet
4
- if TYPE_CHECKING:
5
- from .textual_adaptor import TextualAdaptor
6
-
7
-
8
- class TextualFacet(Facet):
9
- adaptor: "TextualAdaptor"
10
-
11
- def __init__(self, *args, **kwargs):
12
- super().__init__(*args, **kwargs)
13
- # Since TextualApp turns off, we need to have its values stored somewhere
14
- self._title = ""
15
-
16
- # NOTE: multiline title will not show up
17
- def set_title(self, title: str):
18
- self._title = title
19
- try:
20
- self.adaptor.app.title = title
21
- except:
22
- # NOTE: When you receive Facet in Command.init, the app does not exist yet
23
- warn("Setting textual title not implemented well.")
24
-
25
- def submit(self, *args, **kwargs):
26
- super().submit(*args, **kwargs)
27
- try:
28
- self.adaptor.app.action_confirm()
29
- except:
30
- # NOTE: When you receive Facet in Command.init, the app does not exist yet
31
- warn("Setting textual title not implemented well.")
File without changes