ezinput.ezinput_prompt

  1import os
  2import yaml
  3from prompt_toolkit import prompt
  4from prompt_toolkit.completion import WordCompleter, PathCompleter
  5from prompt_toolkit.validation import Validator, ValidationError
  6from pathlib import Path
  7
  8from typing import Optional
  9
 10"""
 11A module to help simplify the create of GUIs in terminals using python prompt-toolkit.
 12"""
 13
 14
 15CONFIG_PATH = Path.home() / ".ezinput"
 16
 17if not os.path.exists(CONFIG_PATH):
 18    os.makedirs(CONFIG_PATH)
 19
 20
 21class Element:
 22    def __init__(self, value):
 23        self.value = value
 24
 25
 26class EZInputPrompt:
 27    """
 28    A class to create terminal-based GUIs using `prompt_toolkit`.
 29
 30    Parameters
 31    ----------
 32    title : str
 33        Title of the GUI, used to store settings.
 34    """
 35
 36    def __init__(self, title: str):
 37        """
 38        Initialize the GUI.
 39
 40        Parameters
 41        ----------
 42        title : str
 43            Title of the GUI.
 44        """
 45        pass
 46
 47    def __getvalue__(self, tag: str):
 48        """
 49        @unified
 50        Get the value of a widget.
 51
 52        Parameters
 53        ----------
 54        tag : str
 55            Tag to identify the widget.
 56
 57        Returns
 58        -------
 59        Any
 60            The value of the widget.
 61        """
 62        return self.elements[tag].value
 63
 64    def add_label(self, value: str = ""):
 65        """
 66        @unified
 67        Add a header to the GUI.
 68
 69        Parameters
 70        ----------
 71        tag : str
 72            Tag to identify the widget.
 73        label : str
 74            The label text to display.
 75        """
 76        self._nLabels += 1
 77        self.cfg[f"label_{self._nLabels}"] = value
 78        self.elements[f"label_{self._nLabels}"] = Element(
 79            self.cfg[f"label_{self._nLabels}"]
 80        )
 81        print("-" * len(value))
 82        print(value)
 83        print("-" * len(value))
 84
 85    def add_text(
 86        self,
 87        tag: str,
 88        description: str,
 89        placeholder: str = "",
 90        *args,
 91        remember_value=False,
 92        **kwargs,
 93    ) -> str:
 94        """
 95        @unified
 96        Add a text prompt to the GUI.
 97
 98        Parameters
 99        ----------
100        tag : str
101            Tag to identify the widget.
102        description : str
103            The message to display.
104        placeholder : str, optional
105            Placeholder text for the input field. Defaults to "".
106        remember_value : bool, optional
107            Whether to remember the last entered value. Defaults to False.
108        *args : tuple
109            Additional positional arguments for the `prompt` function.
110        **kwargs : dict
111            Additional keyword arguments for the `prompt` function.
112
113        Returns
114        -------
115        str
116            The text entered by the user.
117        """
118        if placeholder:
119            kwargs["default"] = placeholder
120        if self.params is not None:
121            if tag in self.params:
122                kwargs["default"] = self.params[tag]
123        elif remember_value and tag in self.cfg:
124            kwargs["default"] = self.cfg[tag]
125        value = prompt(message=description + ": ", *args, **kwargs)  # type: ignore[misc]
126        self.cfg[tag] = value
127        self.elements[tag] = Element(self.cfg[tag])
128        return self.elements[tag]
129
130    def add_callback(
131        self, tag, func, values: dict, description="Run", *args, **kwargs
132    ):
133        """
134        @unified
135        Add a button widget to the container.
136
137        Parameters
138        ----------
139        tag : str
140            Tag to identify the widget.
141        func : callable
142            The function to call when the button is clicked.
143        values : dict
144            Dictionary of widget values to pass to the callback function.
145        description : str, optional
146            The label for the button. Defaults to "Run".
147        *args : tuple
148            Additional positional arguments for the button.
149        **kwargs : dict
150            Additional keyword arguments for the button.
151        """
152        self.save_settings()
153        func(values)
154
155    def add_text_area(
156        self,
157        tag: str,
158        description: str,
159        placeholder: str = "",
160        *args,
161        remember_value=False,
162        **kwargs,
163    ) -> str:
164        """
165        @unified
166        Add a text area prompt to the GUI.
167
168        Parameters
169        ----------
170        tag : str
171            Tag to identify the widget.
172        description : str
173            The message to display.
174        placeholder : str, optional
175            Placeholder text for the input field. Defaults to "".
176        remember_value : bool, optional
177            Whether to remember the last entered value. Defaults to False.
178        *args : tuple
179            Additional positional arguments for the `prompt` function.
180        **kwargs : dict
181            Additional keyword arguments for the `prompt` function.
182
183        Returns
184        -------
185        str
186            The text entered by the user.
187        """
188        if placeholder:
189            kwargs["default"] = placeholder
190        if self.params is not None:
191            if tag in self.params:
192                kwargs["default"] = str(self.params[tag])
193        elif remember_value and tag in self.cfg:
194            kwargs["default"] = self.cfg[tag]
195        value = prompt(message=description + ": ", *args, **kwargs)  # type: ignore[misc]
196        self.cfg[tag] = value
197        self.elements[tag] = Element(self.cfg[tag])
198        return self.elements[tag]
199
200    def add_float_range(
201        self,
202        tag: str,
203        description: str,
204        vmin: float,
205        vmax: float,
206        *args,
207        remember_value=False,
208        **kwargs,
209    ) -> float:
210        """
211        @unified
212        Add a float range prompt to the GUI.
213
214        Parameters
215        ----------
216        tag : str
217            Tag to identify the widget.
218        description : str
219            The message to display.
220        vmin : float
221            Minimum value of the range.
222        vmax : float
223            Maximum value of the range.
224        remember_value : bool, optional
225            Whether to remember the last entered value. Defaults to False.
226        *args : tuple
227            Additional positional arguments for the `prompt` function.
228        **kwargs : dict
229            Additional keyword arguments for the `prompt` function.
230
231        Returns
232        -------
233        float
234            The float value entered by the user.
235        """
236        if "default" in kwargs and isinstance(kwargs["default"], int):
237            kwargs["default"] = str(kwargs["default"])
238
239        if self.params is not None:
240            if tag in self.params:
241                kwargs["default"] = str(self.params[tag])
242        elif remember_value and tag in self.cfg:
243            kwargs["default"] = str(self.cfg[tag])
244
245        value = prompt(  # type: ignore[misc]
246            *args,
247            message=description + f" ({vmin}-{vmax}): ",
248            validator=Validator.from_callable(
249                lambda x: x.strip() != ""
250                and x.replace(".", "", 1).isdigit()
251                and vmin <= float(x) <= vmax,
252                error_message=f"Please enter a valid number ({vmin}-{vmax}).",
253                move_cursor_to_end=True,
254            ),
255            **kwargs,
256        )
257        self.cfg[tag] = float(value)
258        self.elements[tag] = Element(self.cfg[tag])
259        return self.elements[tag]
260
261    def add_int_range(
262        self,
263        tag: str,
264        description: str,
265        vmin: int,
266        vmax: int,
267        *args,
268        remember_value=False,
269        **kwargs,
270    ) -> int:
271        """
272        @unified
273        Add an integer range prompt to the GUI.
274
275        Parameters
276        ----------
277        tag : str
278            Tag to identify the widget.
279        description : str
280            The message to display.
281        vmin : int
282            Minimum value of the range.
283        vmax : int
284            Maximum value of the range.
285        remember_value : bool, optional
286            Whether to remember the last entered value. Defaults to False.
287        *args : tuple
288            Additional positional arguments for the `prompt` function.
289        **kwargs : dict
290            Additional keyword arguments for the `prompt` function.
291
292        Returns
293        -------
294        int
295            The integer value entered by the user.
296        """
297        if "default" in kwargs and isinstance(kwargs["default"], int):
298            kwargs["default"] = str(kwargs["default"])
299
300        if self.params is not None:
301            if tag in self.params:
302                kwargs["default"] = str(self.params[tag])
303        elif remember_value and tag in self.cfg:
304            kwargs["default"] = str(self.cfg[tag])
305
306        value = prompt(  # type: ignore[misc]
307            *args,
308            message=description + f" ({vmin}-{vmax}): ",
309            validator=Validator.from_callable(
310                lambda x: x.strip() != ""
311                and x.isdigit()
312                and vmin <= int(x) <= vmax,
313                error_message=f"Please enter a valid number ({vmin}-{vmax}).",
314                move_cursor_to_end=True,
315            ),
316            **kwargs,
317        )
318        self.cfg[tag] = int(value)
319        self.elements[tag] = Element(self.cfg[tag])
320        return self.elements[tag]
321
322    def add_check(
323        self,
324        tag: str,
325        description: str,
326        *args,
327        remember_value=False,
328        **kwargs,
329    ) -> bool:
330        """
331        @unified
332        Add a yes/no prompt to the GUI.
333
334        Parameters
335        ----------
336        tag : str
337            Tag to identify the widget.
338        description : str
339            The message to display.
340        remember_value : bool, optional
341            Whether to remember the last entered value. Defaults to False.
342        *args : tuple
343            Additional positional arguments for the `prompt` function.
344        **kwargs : dict
345            Additional keyword arguments for the `prompt` function.
346
347        Returns
348        -------
349        bool
350            True if "yes" is selected, False otherwise.
351        """
352        if "default" in kwargs and isinstance(kwargs["default"], bool):
353            kwargs["default"] = "yes" if kwargs["default"] else "no"
354
355        if self.params is not None:
356            if tag in self.params:
357                if self.params[tag]:
358                    kwargs["default"] = "yes"
359                else:
360                    kwargs["default"] = "no"
361        elif remember_value and tag in self.cfg:
362            if self.cfg[tag]:
363                kwargs["default"] = "yes"
364            else:
365                kwargs["default"] = "no"
366
367        value = prompt(  # type: ignore[misc]
368            *args,
369            message=description + " (yes/no): ",
370            completer=WordCompleter(["yes", "no"]),
371            validator=Validator.from_callable(
372                lambda x: x in ["yes", "no"],
373                error_message="Please enter 'yes' or 'no'.",
374                move_cursor_to_end=True,
375            ),
376            **kwargs,
377        )
378        self.cfg[tag] = value.lower() == "yes"
379        self.elements[tag] = Element(self.cfg[tag])
380        return self.elements[tag]
381
382    def add_int_text(
383        self,
384        tag: str,
385        description: str = "",
386        *args,
387        remember_value=False,
388        **kwargs,
389    ) -> int:
390        """
391        @unified
392        Add an integer prompt to the GUI.
393
394        Parameters
395        ----------
396        tag : str
397            Tag to identify the widget.
398        description : str
399            The message to display.
400        remember_value : bool, optional
401            Whether to remember the last entered value. Defaults to False.
402        *args : tuple
403            Additional positional arguments for the `prompt` function.
404        **kwargs : dict
405            Additional keyword arguments for the `prompt` function.
406
407        Returns
408        -------
409        int
410            The integer value entered by the user.
411        """
412        print("ahhhhhhhhh")
413        if "default" in kwargs and isinstance(kwargs["default"], int):
414            kwargs["default"] = str(kwargs["default"])
415
416        if self.params is not None:
417            if tag in self.params:
418                kwargs["default"] = str(self.params[tag])
419        elif remember_value and tag in self.cfg:
420            kwargs["default"] = str(self.cfg[tag])
421        value = prompt(  # type: ignore[misc]
422            *args,
423            message=description + ": ",
424            validator=Validator.from_callable(
425                lambda x: x.isdigit(),
426                error_message="Please enter a valid number.",
427                move_cursor_to_end=True,
428            ),
429            **kwargs,
430        )
431        self.cfg[tag] = int(value)
432        self.elements[tag] = Element(self.cfg[tag])
433        print("ahhhhhhhhh")
434        return self.elements[tag]
435
436    def add_bounded_int_text(
437        self,
438        tag: str,
439        description: str,
440        vmin: int,
441        vmax: int,
442        *args,
443        remember_value=False,
444        **kwargs,
445    ) -> int:
446        """
447        @unified
448        Add an integer range prompt to the GUI.
449
450        Parameters
451        ----------
452        tag : str
453            Tag to identify the widget.
454        description : str
455            The message to display.
456        vmin : int
457            Minimum value of the range.
458        vmax : int
459            Maximum value of the range.
460        remember_value : bool, optional
461            Whether to remember the last entered value. Defaults to False.
462        *args : tuple
463            Additional positional arguments for the `prompt` function.
464        **kwargs : dict
465            Additional keyword arguments for the `prompt` function.
466
467        Returns
468        -------
469        int
470            The integer value entered by the user.
471        """
472        if "default" in kwargs and isinstance(kwargs["default"], int):
473            kwargs["default"] = str(kwargs["default"])
474
475        if self.params is not None:
476            if tag in self.params:
477                kwargs["default"] = str(self.params[tag])
478        elif remember_value and tag in self.cfg:
479            kwargs["default"] = str(self.cfg[tag])
480
481        value = prompt(  # type: ignore[misc]
482            *args,
483            message=description + f" ({vmin}-{vmax}): ",
484            validator=Validator.from_callable(
485                lambda x: x.strip() != ""
486                and x.isdigit()
487                and vmin <= int(x) <= vmax,
488                error_message=f"Please enter a valid number ({vmin}-{vmax}).",
489                move_cursor_to_end=True,
490            ),
491            **kwargs,
492        )
493        self.cfg[tag] = int(value)
494        self.elements[tag] = Element(self.cfg[tag])
495        return self.elements[tag]
496
497    def add_float_text(
498        self,
499        tag: str,
500        description: str = "",
501        *args,
502        remember_value=False,
503        **kwargs,
504    ) -> float:
505        """
506        @unified
507        Add an integer prompt to the GUI.
508
509        Parameters
510        ----------
511        tag : str
512            Tag to identify the widget.
513        description : str
514            The message to display.
515        remember_value : bool, optional
516            Whether to remember the last entered value. Defaults to False.
517        *args : tuple
518            Additional positional arguments for the `prompt` function.
519        **kwargs : dict
520            Additional keyword arguments for the `prompt` function.
521
522        Returns
523        -------
524        float
525            The float value entered by the user.
526        """
527        if "default" in kwargs and isinstance(kwargs["default"], float):
528            kwargs["default"] = str(kwargs["default"])
529
530        if self.params is not None:
531            if tag in self.params:
532                kwargs["default"] = str(self.params[tag])
533        elif remember_value and tag in self.cfg:
534            kwargs["default"] = str(self.cfg[tag])
535        value = prompt(  # type: ignore[misc]
536            *args,
537            message=description + ": ",
538            validator=Validator.from_callable(
539                lambda x: x.replace(".", "", 1).isdigit(),
540                error_message="Please enter a valid number.",
541                move_cursor_to_end=True,
542            ),
543            **kwargs,
544        )
545        self.cfg[tag] = float(value)
546        self.elements[tag] = Element(self.cfg[tag])
547        return self.elements[tag]
548
549    def add_bounded_float_text(
550        self,
551        tag: str,
552        description: str,
553        vmin: float,
554        vmax: float,
555        *args,
556        remember_value=False,
557        **kwargs,
558    ) -> float:
559        """
560        @unified
561        Add an integer range prompt to the GUI.
562
563        Parameters
564        ----------
565        tag : str
566            Tag to identify the widget.
567        description : str
568            The message to display.
569        vmin : float
570            Minimum value of the range.
571        vmax : float
572            Maximum value of the range.
573        remember_value : bool, optional
574            Whether to remember the last entered value. Defaults to False.
575        *args : tuple
576            Additional positional arguments for the `prompt` function.
577        **kwargs : dict
578            Additional keyword arguments for the `prompt` function.
579
580        Returns
581        -------
582        float
583            The float value entered by the user.
584        """
585        if "default" in kwargs and isinstance(kwargs["default"], int):
586            kwargs["default"] = str(kwargs["default"])
587
588        if self.params is not None:
589            if tag in self.params:
590                kwargs["default"] = str(self.params[tag])
591        elif remember_value and tag in self.cfg:
592            kwargs["default"] = str(self.cfg[tag])
593
594        value = prompt(  # type: ignore[misc]
595            *args,
596            message=description + f" ({vmin}-{vmax}): ",
597            validator=Validator.from_callable(
598                lambda x: x.strip() != ""
599                and x.replace(".", "", 1).isdigit()
600                and vmin <= float(x) <= vmax,
601                error_message=f"Please enter a valid number ({vmin}-{vmax}).",
602                move_cursor_to_end=True,
603            ),
604            **kwargs,
605        )
606        self.cfg[tag] = float(value)
607        self.elements[tag] = Element(self.cfg[tag])
608        return self.elements[tag]
609
610    def add_dropdown(
611        self,
612        tag: str,
613        options: list,
614        description: str = "",
615        *args,
616        remember_value=False,
617        **kwargs,
618    ) -> str:
619        """
620        @unified
621        Add a dropdown prompt to the GUI.
622
623        Parameters
624        ----------
625        tag : str
626            Tag to identify the widget.
627        description : str
628            The message to display.
629        options : list
630            List of choices for the dropdown.
631        remember_value : bool, optional
632            Whether to remember the last selected value. Defaults to False.
633        *args : tuple
634            Additional positional arguments for the `prompt` function.
635        **kwargs : dict
636            Additional keyword arguments for the `prompt` function.
637
638        Returns
639        -------
640        str
641            The selected choice.
642        """
643        if self.params is not None:
644            if tag in self.params:
645                kwargs["default"] = self.params[tag]
646        elif remember_value and tag in self.cfg:
647            kwargs["default"] = self.cfg[tag]
648
649        value = prompt(  # type: ignore[misc]
650            *args,
651            message=description + ": ",
652            completer=WordCompleter(options),
653            validator=Validator.from_callable(
654                lambda x: x in options,
655                error_message="Please select a valid choice from the dropdown.",
656                move_cursor_to_end=True,
657            ),
658            **kwargs,
659        )
660        self.cfg[tag] = value
661        self.elements[tag] = Element(self.cfg[tag])
662        return self.elements[tag]
663
664    def add_path_completer(
665        self, tag: str, description: str, *args, remember_value=False, **kwargs
666    ) -> Path:
667        """
668        @prompt
669        Add a path completer to the GUI.
670
671        Parameters
672        ----------
673        tag : str
674            Tag to identify the widget.
675        description : str
676            The message to display.
677        remember_value : bool, optional
678            Whether to remember the last entered path. Defaults to False.
679        *args : tuple
680            Additional positional arguments for the `prompt` function.
681        **kwargs : dict
682            Additional keyword arguments for the `prompt` function.
683
684        Returns
685        -------
686        Path
687            The path entered by the user.
688        """
689        if self.params is not None:
690            if tag in self.params:
691                kwargs["default"] = str(self.params[tag])
692        elif remember_value and tag in self.cfg:
693            kwargs["default"] = str(self.cfg[tag])
694
695        value = prompt(  # type: ignore[misc]
696            *args,
697            message=description + ": ",
698            completer=PathCompleter(),
699            validator=Validator.from_callable(
700                lambda x: Path(x).exists(),
701                error_message="Please enter a valid path.",
702                move_cursor_to_end=True,
703            ),
704            **kwargs,
705        )
706        self.cfg[tag] = Path(value)
707        self.elements[tag] = Element(self.cfg[tag])
708        return self.elements[tag]
709
710    def add_output(self, tag: str, *args, **kwargs):
711        """
712        @unified
713        Does nothing in the terminal-based GUI.
714
715        Parameters
716        ----------
717        tag : str
718            Tag to identify the widget.
719        *args : tuple
720            Additional positional arguments for the widget.
721        **kwargs : dict
722            Additional keyword arguments for the widget.
723        """
724        pass
725
726    def clear_elements(self):
727        """
728        @unified
729        Clear all elements from the GUI.
730        """
731        self.elements = {}
732
733    def save_parameters(self, path: str):
734        """
735        @unified
736        Save the widget values to a file.
737
738        Parameters
739        ----------
740        path : str
741            The path to save the file.
742        """
743        if not path.endswith(".yml"):
744            path += f"{self.title}_parameters.yml"
745        out = {}
746        for tag in self.elements:
747            if tag.startswith("label_"):
748                pass
749            elif hasattr(self.elements[tag], "value"):
750                out[tag] = self.elements[tag].value
751        with open(path, "w") as f:
752            yaml.dump(out, f)
753
754    def save_settings(self):
755        """
756        @unified
757        Save the widget values to the configuration file.
758        """
759        for tag in self.elements:
760            if tag.startswith("label_"):
761                pass
762            elif hasattr(self.elements[tag], "value"):
763                self.cfg[tag] = self.elements[tag].value
764        config_file = CONFIG_PATH / f"{self.title}.yml"
765        config_file.parent.mkdir(exist_ok=True)
766
767        base_config = self._get_config(self.title)  # loads the config file
768        for key, value in self.cfg.items():
769            base_config[key] = value
770
771        with open(config_file, "w") as f:
772            yaml.dump(base_config, f)
773
774    def show(self):
775        """
776        @unified
777        Display the GUI. (No-op for terminal-based GUIs.)
778        """
779        pass
780
781    def _get_config(self, title: Optional[str]) -> dict:
782        """
783        Get the configuration dictionary without needing to initialize the GUI.
784
785        Parameters
786        ----------
787        title : str, optional
788            The title of the GUI. If None, returns the entire configuration.
789
790        Returns
791        -------
792        dict
793            The configuration dictionary.
794        """
795
796        config_file = CONFIG_PATH / f"{title}.yml"
797
798        if not config_file.exists():
799            return {}
800
801        with open(config_file, "r") as f:
802            return yaml.load(f, Loader=yaml.SafeLoader)
CONFIG_PATH = PosixPath('/home/runner/.ezinput')
class Element:
22class Element:
23    def __init__(self, value):
24        self.value = value
Element(value)
23    def __init__(self, value):
24        self.value = value
value
class EZInputPrompt:
 27class EZInputPrompt:
 28    """
 29    A class to create terminal-based GUIs using `prompt_toolkit`.
 30
 31    Parameters
 32    ----------
 33    title : str
 34        Title of the GUI, used to store settings.
 35    """
 36
 37    def __init__(self, title: str):
 38        """
 39        Initialize the GUI.
 40
 41        Parameters
 42        ----------
 43        title : str
 44            Title of the GUI.
 45        """
 46        pass
 47
 48    def __getvalue__(self, tag: str):
 49        """
 50        @unified
 51        Get the value of a widget.
 52
 53        Parameters
 54        ----------
 55        tag : str
 56            Tag to identify the widget.
 57
 58        Returns
 59        -------
 60        Any
 61            The value of the widget.
 62        """
 63        return self.elements[tag].value
 64
 65    def add_label(self, value: str = ""):
 66        """
 67        @unified
 68        Add a header to the GUI.
 69
 70        Parameters
 71        ----------
 72        tag : str
 73            Tag to identify the widget.
 74        label : str
 75            The label text to display.
 76        """
 77        self._nLabels += 1
 78        self.cfg[f"label_{self._nLabels}"] = value
 79        self.elements[f"label_{self._nLabels}"] = Element(
 80            self.cfg[f"label_{self._nLabels}"]
 81        )
 82        print("-" * len(value))
 83        print(value)
 84        print("-" * len(value))
 85
 86    def add_text(
 87        self,
 88        tag: str,
 89        description: str,
 90        placeholder: str = "",
 91        *args,
 92        remember_value=False,
 93        **kwargs,
 94    ) -> str:
 95        """
 96        @unified
 97        Add a text prompt to the GUI.
 98
 99        Parameters
100        ----------
101        tag : str
102            Tag to identify the widget.
103        description : str
104            The message to display.
105        placeholder : str, optional
106            Placeholder text for the input field. Defaults to "".
107        remember_value : bool, optional
108            Whether to remember the last entered value. Defaults to False.
109        *args : tuple
110            Additional positional arguments for the `prompt` function.
111        **kwargs : dict
112            Additional keyword arguments for the `prompt` function.
113
114        Returns
115        -------
116        str
117            The text entered by the user.
118        """
119        if placeholder:
120            kwargs["default"] = placeholder
121        if self.params is not None:
122            if tag in self.params:
123                kwargs["default"] = self.params[tag]
124        elif remember_value and tag in self.cfg:
125            kwargs["default"] = self.cfg[tag]
126        value = prompt(message=description + ": ", *args, **kwargs)  # type: ignore[misc]
127        self.cfg[tag] = value
128        self.elements[tag] = Element(self.cfg[tag])
129        return self.elements[tag]
130
131    def add_callback(
132        self, tag, func, values: dict, description="Run", *args, **kwargs
133    ):
134        """
135        @unified
136        Add a button widget to the container.
137
138        Parameters
139        ----------
140        tag : str
141            Tag to identify the widget.
142        func : callable
143            The function to call when the button is clicked.
144        values : dict
145            Dictionary of widget values to pass to the callback function.
146        description : str, optional
147            The label for the button. Defaults to "Run".
148        *args : tuple
149            Additional positional arguments for the button.
150        **kwargs : dict
151            Additional keyword arguments for the button.
152        """
153        self.save_settings()
154        func(values)
155
156    def add_text_area(
157        self,
158        tag: str,
159        description: str,
160        placeholder: str = "",
161        *args,
162        remember_value=False,
163        **kwargs,
164    ) -> str:
165        """
166        @unified
167        Add a text area prompt to the GUI.
168
169        Parameters
170        ----------
171        tag : str
172            Tag to identify the widget.
173        description : str
174            The message to display.
175        placeholder : str, optional
176            Placeholder text for the input field. Defaults to "".
177        remember_value : bool, optional
178            Whether to remember the last entered value. Defaults to False.
179        *args : tuple
180            Additional positional arguments for the `prompt` function.
181        **kwargs : dict
182            Additional keyword arguments for the `prompt` function.
183
184        Returns
185        -------
186        str
187            The text entered by the user.
188        """
189        if placeholder:
190            kwargs["default"] = placeholder
191        if self.params is not None:
192            if tag in self.params:
193                kwargs["default"] = str(self.params[tag])
194        elif remember_value and tag in self.cfg:
195            kwargs["default"] = self.cfg[tag]
196        value = prompt(message=description + ": ", *args, **kwargs)  # type: ignore[misc]
197        self.cfg[tag] = value
198        self.elements[tag] = Element(self.cfg[tag])
199        return self.elements[tag]
200
201    def add_float_range(
202        self,
203        tag: str,
204        description: str,
205        vmin: float,
206        vmax: float,
207        *args,
208        remember_value=False,
209        **kwargs,
210    ) -> float:
211        """
212        @unified
213        Add a float range prompt to the GUI.
214
215        Parameters
216        ----------
217        tag : str
218            Tag to identify the widget.
219        description : str
220            The message to display.
221        vmin : float
222            Minimum value of the range.
223        vmax : float
224            Maximum value of the range.
225        remember_value : bool, optional
226            Whether to remember the last entered value. Defaults to False.
227        *args : tuple
228            Additional positional arguments for the `prompt` function.
229        **kwargs : dict
230            Additional keyword arguments for the `prompt` function.
231
232        Returns
233        -------
234        float
235            The float value entered by the user.
236        """
237        if "default" in kwargs and isinstance(kwargs["default"], int):
238            kwargs["default"] = str(kwargs["default"])
239
240        if self.params is not None:
241            if tag in self.params:
242                kwargs["default"] = str(self.params[tag])
243        elif remember_value and tag in self.cfg:
244            kwargs["default"] = str(self.cfg[tag])
245
246        value = prompt(  # type: ignore[misc]
247            *args,
248            message=description + f" ({vmin}-{vmax}): ",
249            validator=Validator.from_callable(
250                lambda x: x.strip() != ""
251                and x.replace(".", "", 1).isdigit()
252                and vmin <= float(x) <= vmax,
253                error_message=f"Please enter a valid number ({vmin}-{vmax}).",
254                move_cursor_to_end=True,
255            ),
256            **kwargs,
257        )
258        self.cfg[tag] = float(value)
259        self.elements[tag] = Element(self.cfg[tag])
260        return self.elements[tag]
261
262    def add_int_range(
263        self,
264        tag: str,
265        description: str,
266        vmin: int,
267        vmax: int,
268        *args,
269        remember_value=False,
270        **kwargs,
271    ) -> int:
272        """
273        @unified
274        Add an integer range prompt to the GUI.
275
276        Parameters
277        ----------
278        tag : str
279            Tag to identify the widget.
280        description : str
281            The message to display.
282        vmin : int
283            Minimum value of the range.
284        vmax : int
285            Maximum value of the range.
286        remember_value : bool, optional
287            Whether to remember the last entered value. Defaults to False.
288        *args : tuple
289            Additional positional arguments for the `prompt` function.
290        **kwargs : dict
291            Additional keyword arguments for the `prompt` function.
292
293        Returns
294        -------
295        int
296            The integer value entered by the user.
297        """
298        if "default" in kwargs and isinstance(kwargs["default"], int):
299            kwargs["default"] = str(kwargs["default"])
300
301        if self.params is not None:
302            if tag in self.params:
303                kwargs["default"] = str(self.params[tag])
304        elif remember_value and tag in self.cfg:
305            kwargs["default"] = str(self.cfg[tag])
306
307        value = prompt(  # type: ignore[misc]
308            *args,
309            message=description + f" ({vmin}-{vmax}): ",
310            validator=Validator.from_callable(
311                lambda x: x.strip() != ""
312                and x.isdigit()
313                and vmin <= int(x) <= vmax,
314                error_message=f"Please enter a valid number ({vmin}-{vmax}).",
315                move_cursor_to_end=True,
316            ),
317            **kwargs,
318        )
319        self.cfg[tag] = int(value)
320        self.elements[tag] = Element(self.cfg[tag])
321        return self.elements[tag]
322
323    def add_check(
324        self,
325        tag: str,
326        description: str,
327        *args,
328        remember_value=False,
329        **kwargs,
330    ) -> bool:
331        """
332        @unified
333        Add a yes/no prompt to the GUI.
334
335        Parameters
336        ----------
337        tag : str
338            Tag to identify the widget.
339        description : str
340            The message to display.
341        remember_value : bool, optional
342            Whether to remember the last entered value. Defaults to False.
343        *args : tuple
344            Additional positional arguments for the `prompt` function.
345        **kwargs : dict
346            Additional keyword arguments for the `prompt` function.
347
348        Returns
349        -------
350        bool
351            True if "yes" is selected, False otherwise.
352        """
353        if "default" in kwargs and isinstance(kwargs["default"], bool):
354            kwargs["default"] = "yes" if kwargs["default"] else "no"
355
356        if self.params is not None:
357            if tag in self.params:
358                if self.params[tag]:
359                    kwargs["default"] = "yes"
360                else:
361                    kwargs["default"] = "no"
362        elif remember_value and tag in self.cfg:
363            if self.cfg[tag]:
364                kwargs["default"] = "yes"
365            else:
366                kwargs["default"] = "no"
367
368        value = prompt(  # type: ignore[misc]
369            *args,
370            message=description + " (yes/no): ",
371            completer=WordCompleter(["yes", "no"]),
372            validator=Validator.from_callable(
373                lambda x: x in ["yes", "no"],
374                error_message="Please enter 'yes' or 'no'.",
375                move_cursor_to_end=True,
376            ),
377            **kwargs,
378        )
379        self.cfg[tag] = value.lower() == "yes"
380        self.elements[tag] = Element(self.cfg[tag])
381        return self.elements[tag]
382
383    def add_int_text(
384        self,
385        tag: str,
386        description: str = "",
387        *args,
388        remember_value=False,
389        **kwargs,
390    ) -> int:
391        """
392        @unified
393        Add an integer prompt to the GUI.
394
395        Parameters
396        ----------
397        tag : str
398            Tag to identify the widget.
399        description : str
400            The message to display.
401        remember_value : bool, optional
402            Whether to remember the last entered value. Defaults to False.
403        *args : tuple
404            Additional positional arguments for the `prompt` function.
405        **kwargs : dict
406            Additional keyword arguments for the `prompt` function.
407
408        Returns
409        -------
410        int
411            The integer value entered by the user.
412        """
413        print("ahhhhhhhhh")
414        if "default" in kwargs and isinstance(kwargs["default"], int):
415            kwargs["default"] = str(kwargs["default"])
416
417        if self.params is not None:
418            if tag in self.params:
419                kwargs["default"] = str(self.params[tag])
420        elif remember_value and tag in self.cfg:
421            kwargs["default"] = str(self.cfg[tag])
422        value = prompt(  # type: ignore[misc]
423            *args,
424            message=description + ": ",
425            validator=Validator.from_callable(
426                lambda x: x.isdigit(),
427                error_message="Please enter a valid number.",
428                move_cursor_to_end=True,
429            ),
430            **kwargs,
431        )
432        self.cfg[tag] = int(value)
433        self.elements[tag] = Element(self.cfg[tag])
434        print("ahhhhhhhhh")
435        return self.elements[tag]
436
437    def add_bounded_int_text(
438        self,
439        tag: str,
440        description: str,
441        vmin: int,
442        vmax: int,
443        *args,
444        remember_value=False,
445        **kwargs,
446    ) -> int:
447        """
448        @unified
449        Add an integer range prompt to the GUI.
450
451        Parameters
452        ----------
453        tag : str
454            Tag to identify the widget.
455        description : str
456            The message to display.
457        vmin : int
458            Minimum value of the range.
459        vmax : int
460            Maximum value of the range.
461        remember_value : bool, optional
462            Whether to remember the last entered value. Defaults to False.
463        *args : tuple
464            Additional positional arguments for the `prompt` function.
465        **kwargs : dict
466            Additional keyword arguments for the `prompt` function.
467
468        Returns
469        -------
470        int
471            The integer value entered by the user.
472        """
473        if "default" in kwargs and isinstance(kwargs["default"], int):
474            kwargs["default"] = str(kwargs["default"])
475
476        if self.params is not None:
477            if tag in self.params:
478                kwargs["default"] = str(self.params[tag])
479        elif remember_value and tag in self.cfg:
480            kwargs["default"] = str(self.cfg[tag])
481
482        value = prompt(  # type: ignore[misc]
483            *args,
484            message=description + f" ({vmin}-{vmax}): ",
485            validator=Validator.from_callable(
486                lambda x: x.strip() != ""
487                and x.isdigit()
488                and vmin <= int(x) <= vmax,
489                error_message=f"Please enter a valid number ({vmin}-{vmax}).",
490                move_cursor_to_end=True,
491            ),
492            **kwargs,
493        )
494        self.cfg[tag] = int(value)
495        self.elements[tag] = Element(self.cfg[tag])
496        return self.elements[tag]
497
498    def add_float_text(
499        self,
500        tag: str,
501        description: str = "",
502        *args,
503        remember_value=False,
504        **kwargs,
505    ) -> float:
506        """
507        @unified
508        Add an integer prompt to the GUI.
509
510        Parameters
511        ----------
512        tag : str
513            Tag to identify the widget.
514        description : str
515            The message to display.
516        remember_value : bool, optional
517            Whether to remember the last entered value. Defaults to False.
518        *args : tuple
519            Additional positional arguments for the `prompt` function.
520        **kwargs : dict
521            Additional keyword arguments for the `prompt` function.
522
523        Returns
524        -------
525        float
526            The float value entered by the user.
527        """
528        if "default" in kwargs and isinstance(kwargs["default"], float):
529            kwargs["default"] = str(kwargs["default"])
530
531        if self.params is not None:
532            if tag in self.params:
533                kwargs["default"] = str(self.params[tag])
534        elif remember_value and tag in self.cfg:
535            kwargs["default"] = str(self.cfg[tag])
536        value = prompt(  # type: ignore[misc]
537            *args,
538            message=description + ": ",
539            validator=Validator.from_callable(
540                lambda x: x.replace(".", "", 1).isdigit(),
541                error_message="Please enter a valid number.",
542                move_cursor_to_end=True,
543            ),
544            **kwargs,
545        )
546        self.cfg[tag] = float(value)
547        self.elements[tag] = Element(self.cfg[tag])
548        return self.elements[tag]
549
550    def add_bounded_float_text(
551        self,
552        tag: str,
553        description: str,
554        vmin: float,
555        vmax: float,
556        *args,
557        remember_value=False,
558        **kwargs,
559    ) -> float:
560        """
561        @unified
562        Add an integer range prompt to the GUI.
563
564        Parameters
565        ----------
566        tag : str
567            Tag to identify the widget.
568        description : str
569            The message to display.
570        vmin : float
571            Minimum value of the range.
572        vmax : float
573            Maximum value of the range.
574        remember_value : bool, optional
575            Whether to remember the last entered value. Defaults to False.
576        *args : tuple
577            Additional positional arguments for the `prompt` function.
578        **kwargs : dict
579            Additional keyword arguments for the `prompt` function.
580
581        Returns
582        -------
583        float
584            The float value entered by the user.
585        """
586        if "default" in kwargs and isinstance(kwargs["default"], int):
587            kwargs["default"] = str(kwargs["default"])
588
589        if self.params is not None:
590            if tag in self.params:
591                kwargs["default"] = str(self.params[tag])
592        elif remember_value and tag in self.cfg:
593            kwargs["default"] = str(self.cfg[tag])
594
595        value = prompt(  # type: ignore[misc]
596            *args,
597            message=description + f" ({vmin}-{vmax}): ",
598            validator=Validator.from_callable(
599                lambda x: x.strip() != ""
600                and x.replace(".", "", 1).isdigit()
601                and vmin <= float(x) <= vmax,
602                error_message=f"Please enter a valid number ({vmin}-{vmax}).",
603                move_cursor_to_end=True,
604            ),
605            **kwargs,
606        )
607        self.cfg[tag] = float(value)
608        self.elements[tag] = Element(self.cfg[tag])
609        return self.elements[tag]
610
611    def add_dropdown(
612        self,
613        tag: str,
614        options: list,
615        description: str = "",
616        *args,
617        remember_value=False,
618        **kwargs,
619    ) -> str:
620        """
621        @unified
622        Add a dropdown prompt to the GUI.
623
624        Parameters
625        ----------
626        tag : str
627            Tag to identify the widget.
628        description : str
629            The message to display.
630        options : list
631            List of choices for the dropdown.
632        remember_value : bool, optional
633            Whether to remember the last selected value. Defaults to False.
634        *args : tuple
635            Additional positional arguments for the `prompt` function.
636        **kwargs : dict
637            Additional keyword arguments for the `prompt` function.
638
639        Returns
640        -------
641        str
642            The selected choice.
643        """
644        if self.params is not None:
645            if tag in self.params:
646                kwargs["default"] = self.params[tag]
647        elif remember_value and tag in self.cfg:
648            kwargs["default"] = self.cfg[tag]
649
650        value = prompt(  # type: ignore[misc]
651            *args,
652            message=description + ": ",
653            completer=WordCompleter(options),
654            validator=Validator.from_callable(
655                lambda x: x in options,
656                error_message="Please select a valid choice from the dropdown.",
657                move_cursor_to_end=True,
658            ),
659            **kwargs,
660        )
661        self.cfg[tag] = value
662        self.elements[tag] = Element(self.cfg[tag])
663        return self.elements[tag]
664
665    def add_path_completer(
666        self, tag: str, description: str, *args, remember_value=False, **kwargs
667    ) -> Path:
668        """
669        @prompt
670        Add a path completer to the GUI.
671
672        Parameters
673        ----------
674        tag : str
675            Tag to identify the widget.
676        description : str
677            The message to display.
678        remember_value : bool, optional
679            Whether to remember the last entered path. Defaults to False.
680        *args : tuple
681            Additional positional arguments for the `prompt` function.
682        **kwargs : dict
683            Additional keyword arguments for the `prompt` function.
684
685        Returns
686        -------
687        Path
688            The path entered by the user.
689        """
690        if self.params is not None:
691            if tag in self.params:
692                kwargs["default"] = str(self.params[tag])
693        elif remember_value and tag in self.cfg:
694            kwargs["default"] = str(self.cfg[tag])
695
696        value = prompt(  # type: ignore[misc]
697            *args,
698            message=description + ": ",
699            completer=PathCompleter(),
700            validator=Validator.from_callable(
701                lambda x: Path(x).exists(),
702                error_message="Please enter a valid path.",
703                move_cursor_to_end=True,
704            ),
705            **kwargs,
706        )
707        self.cfg[tag] = Path(value)
708        self.elements[tag] = Element(self.cfg[tag])
709        return self.elements[tag]
710
711    def add_output(self, tag: str, *args, **kwargs):
712        """
713        @unified
714        Does nothing in the terminal-based GUI.
715
716        Parameters
717        ----------
718        tag : str
719            Tag to identify the widget.
720        *args : tuple
721            Additional positional arguments for the widget.
722        **kwargs : dict
723            Additional keyword arguments for the widget.
724        """
725        pass
726
727    def clear_elements(self):
728        """
729        @unified
730        Clear all elements from the GUI.
731        """
732        self.elements = {}
733
734    def save_parameters(self, path: str):
735        """
736        @unified
737        Save the widget values to a file.
738
739        Parameters
740        ----------
741        path : str
742            The path to save the file.
743        """
744        if not path.endswith(".yml"):
745            path += f"{self.title}_parameters.yml"
746        out = {}
747        for tag in self.elements:
748            if tag.startswith("label_"):
749                pass
750            elif hasattr(self.elements[tag], "value"):
751                out[tag] = self.elements[tag].value
752        with open(path, "w") as f:
753            yaml.dump(out, f)
754
755    def save_settings(self):
756        """
757        @unified
758        Save the widget values to the configuration file.
759        """
760        for tag in self.elements:
761            if tag.startswith("label_"):
762                pass
763            elif hasattr(self.elements[tag], "value"):
764                self.cfg[tag] = self.elements[tag].value
765        config_file = CONFIG_PATH / f"{self.title}.yml"
766        config_file.parent.mkdir(exist_ok=True)
767
768        base_config = self._get_config(self.title)  # loads the config file
769        for key, value in self.cfg.items():
770            base_config[key] = value
771
772        with open(config_file, "w") as f:
773            yaml.dump(base_config, f)
774
775    def show(self):
776        """
777        @unified
778        Display the GUI. (No-op for terminal-based GUIs.)
779        """
780        pass
781
782    def _get_config(self, title: Optional[str]) -> dict:
783        """
784        Get the configuration dictionary without needing to initialize the GUI.
785
786        Parameters
787        ----------
788        title : str, optional
789            The title of the GUI. If None, returns the entire configuration.
790
791        Returns
792        -------
793        dict
794            The configuration dictionary.
795        """
796
797        config_file = CONFIG_PATH / f"{title}.yml"
798
799        if not config_file.exists():
800            return {}
801
802        with open(config_file, "r") as f:
803            return yaml.load(f, Loader=yaml.SafeLoader)

A class to create terminal-based GUIs using prompt_toolkit.

Parameters

title : str Title of the GUI, used to store settings.

EZInputPrompt(title: str)
37    def __init__(self, title: str):
38        """
39        Initialize the GUI.
40
41        Parameters
42        ----------
43        title : str
44            Title of the GUI.
45        """
46        pass

Initialize the GUI.

Parameters

title : str Title of the GUI.

def add_label(self, value: str = ''):
65    def add_label(self, value: str = ""):
66        """
67        @unified
68        Add a header to the GUI.
69
70        Parameters
71        ----------
72        tag : str
73            Tag to identify the widget.
74        label : str
75            The label text to display.
76        """
77        self._nLabels += 1
78        self.cfg[f"label_{self._nLabels}"] = value
79        self.elements[f"label_{self._nLabels}"] = Element(
80            self.cfg[f"label_{self._nLabels}"]
81        )
82        print("-" * len(value))
83        print(value)
84        print("-" * len(value))

@unified Add a header to the GUI.

Parameters

tag : str Tag to identify the widget. label : str The label text to display.

def add_text( self, tag: str, description: str, placeholder: str = '', *args, remember_value=False, **kwargs) -> str:
 86    def add_text(
 87        self,
 88        tag: str,
 89        description: str,
 90        placeholder: str = "",
 91        *args,
 92        remember_value=False,
 93        **kwargs,
 94    ) -> str:
 95        """
 96        @unified
 97        Add a text prompt to the GUI.
 98
 99        Parameters
100        ----------
101        tag : str
102            Tag to identify the widget.
103        description : str
104            The message to display.
105        placeholder : str, optional
106            Placeholder text for the input field. Defaults to "".
107        remember_value : bool, optional
108            Whether to remember the last entered value. Defaults to False.
109        *args : tuple
110            Additional positional arguments for the `prompt` function.
111        **kwargs : dict
112            Additional keyword arguments for the `prompt` function.
113
114        Returns
115        -------
116        str
117            The text entered by the user.
118        """
119        if placeholder:
120            kwargs["default"] = placeholder
121        if self.params is not None:
122            if tag in self.params:
123                kwargs["default"] = self.params[tag]
124        elif remember_value and tag in self.cfg:
125            kwargs["default"] = self.cfg[tag]
126        value = prompt(message=description + ": ", *args, **kwargs)  # type: ignore[misc]
127        self.cfg[tag] = value
128        self.elements[tag] = Element(self.cfg[tag])
129        return self.elements[tag]

@unified Add a text prompt to the GUI.

Parameters

tag : str Tag to identify the widget. description : str The message to display. placeholder : str, optional Placeholder text for the input field. Defaults to "". remember_value : bool, optional Whether to remember the last entered value. Defaults to False. args : tuple Additional positional arguments for the prompt function. *kwargs : dict Additional keyword arguments for the prompt function.

Returns

str The text entered by the user.

def add_callback(self, tag, func, values: dict, description='Run', *args, **kwargs):
131    def add_callback(
132        self, tag, func, values: dict, description="Run", *args, **kwargs
133    ):
134        """
135        @unified
136        Add a button widget to the container.
137
138        Parameters
139        ----------
140        tag : str
141            Tag to identify the widget.
142        func : callable
143            The function to call when the button is clicked.
144        values : dict
145            Dictionary of widget values to pass to the callback function.
146        description : str, optional
147            The label for the button. Defaults to "Run".
148        *args : tuple
149            Additional positional arguments for the button.
150        **kwargs : dict
151            Additional keyword arguments for the button.
152        """
153        self.save_settings()
154        func(values)

@unified Add a button widget to the container.

Parameters

tag : str Tag to identify the widget. func : callable The function to call when the button is clicked. values : dict Dictionary of widget values to pass to the callback function. description : str, optional The label for the button. Defaults to "Run". args : tuple Additional positional arguments for the button. *kwargs : dict Additional keyword arguments for the button.

def add_text_area( self, tag: str, description: str, placeholder: str = '', *args, remember_value=False, **kwargs) -> str:
156    def add_text_area(
157        self,
158        tag: str,
159        description: str,
160        placeholder: str = "",
161        *args,
162        remember_value=False,
163        **kwargs,
164    ) -> str:
165        """
166        @unified
167        Add a text area prompt to the GUI.
168
169        Parameters
170        ----------
171        tag : str
172            Tag to identify the widget.
173        description : str
174            The message to display.
175        placeholder : str, optional
176            Placeholder text for the input field. Defaults to "".
177        remember_value : bool, optional
178            Whether to remember the last entered value. Defaults to False.
179        *args : tuple
180            Additional positional arguments for the `prompt` function.
181        **kwargs : dict
182            Additional keyword arguments for the `prompt` function.
183
184        Returns
185        -------
186        str
187            The text entered by the user.
188        """
189        if placeholder:
190            kwargs["default"] = placeholder
191        if self.params is not None:
192            if tag in self.params:
193                kwargs["default"] = str(self.params[tag])
194        elif remember_value and tag in self.cfg:
195            kwargs["default"] = self.cfg[tag]
196        value = prompt(message=description + ": ", *args, **kwargs)  # type: ignore[misc]
197        self.cfg[tag] = value
198        self.elements[tag] = Element(self.cfg[tag])
199        return self.elements[tag]

@unified Add a text area prompt to the GUI.

Parameters

tag : str Tag to identify the widget. description : str The message to display. placeholder : str, optional Placeholder text for the input field. Defaults to "". remember_value : bool, optional Whether to remember the last entered value. Defaults to False. args : tuple Additional positional arguments for the prompt function. *kwargs : dict Additional keyword arguments for the prompt function.

Returns

str The text entered by the user.

def add_float_range( self, tag: str, description: str, vmin: float, vmax: float, *args, remember_value=False, **kwargs) -> float:
201    def add_float_range(
202        self,
203        tag: str,
204        description: str,
205        vmin: float,
206        vmax: float,
207        *args,
208        remember_value=False,
209        **kwargs,
210    ) -> float:
211        """
212        @unified
213        Add a float range prompt to the GUI.
214
215        Parameters
216        ----------
217        tag : str
218            Tag to identify the widget.
219        description : str
220            The message to display.
221        vmin : float
222            Minimum value of the range.
223        vmax : float
224            Maximum value of the range.
225        remember_value : bool, optional
226            Whether to remember the last entered value. Defaults to False.
227        *args : tuple
228            Additional positional arguments for the `prompt` function.
229        **kwargs : dict
230            Additional keyword arguments for the `prompt` function.
231
232        Returns
233        -------
234        float
235            The float value entered by the user.
236        """
237        if "default" in kwargs and isinstance(kwargs["default"], int):
238            kwargs["default"] = str(kwargs["default"])
239
240        if self.params is not None:
241            if tag in self.params:
242                kwargs["default"] = str(self.params[tag])
243        elif remember_value and tag in self.cfg:
244            kwargs["default"] = str(self.cfg[tag])
245
246        value = prompt(  # type: ignore[misc]
247            *args,
248            message=description + f" ({vmin}-{vmax}): ",
249            validator=Validator.from_callable(
250                lambda x: x.strip() != ""
251                and x.replace(".", "", 1).isdigit()
252                and vmin <= float(x) <= vmax,
253                error_message=f"Please enter a valid number ({vmin}-{vmax}).",
254                move_cursor_to_end=True,
255            ),
256            **kwargs,
257        )
258        self.cfg[tag] = float(value)
259        self.elements[tag] = Element(self.cfg[tag])
260        return self.elements[tag]

@unified Add a float range prompt to the GUI.

Parameters

tag : str Tag to identify the widget. description : str The message to display. vmin : float Minimum value of the range. vmax : float Maximum value of the range. remember_value : bool, optional Whether to remember the last entered value. Defaults to False. args : tuple Additional positional arguments for the prompt function. *kwargs : dict Additional keyword arguments for the prompt function.

Returns

float The float value entered by the user.

def add_int_range( self, tag: str, description: str, vmin: int, vmax: int, *args, remember_value=False, **kwargs) -> int:
262    def add_int_range(
263        self,
264        tag: str,
265        description: str,
266        vmin: int,
267        vmax: int,
268        *args,
269        remember_value=False,
270        **kwargs,
271    ) -> int:
272        """
273        @unified
274        Add an integer range prompt to the GUI.
275
276        Parameters
277        ----------
278        tag : str
279            Tag to identify the widget.
280        description : str
281            The message to display.
282        vmin : int
283            Minimum value of the range.
284        vmax : int
285            Maximum value of the range.
286        remember_value : bool, optional
287            Whether to remember the last entered value. Defaults to False.
288        *args : tuple
289            Additional positional arguments for the `prompt` function.
290        **kwargs : dict
291            Additional keyword arguments for the `prompt` function.
292
293        Returns
294        -------
295        int
296            The integer value entered by the user.
297        """
298        if "default" in kwargs and isinstance(kwargs["default"], int):
299            kwargs["default"] = str(kwargs["default"])
300
301        if self.params is not None:
302            if tag in self.params:
303                kwargs["default"] = str(self.params[tag])
304        elif remember_value and tag in self.cfg:
305            kwargs["default"] = str(self.cfg[tag])
306
307        value = prompt(  # type: ignore[misc]
308            *args,
309            message=description + f" ({vmin}-{vmax}): ",
310            validator=Validator.from_callable(
311                lambda x: x.strip() != ""
312                and x.isdigit()
313                and vmin <= int(x) <= vmax,
314                error_message=f"Please enter a valid number ({vmin}-{vmax}).",
315                move_cursor_to_end=True,
316            ),
317            **kwargs,
318        )
319        self.cfg[tag] = int(value)
320        self.elements[tag] = Element(self.cfg[tag])
321        return self.elements[tag]

@unified Add an integer range prompt to the GUI.

Parameters

tag : str Tag to identify the widget. description : str The message to display. vmin : int Minimum value of the range. vmax : int Maximum value of the range. remember_value : bool, optional Whether to remember the last entered value. Defaults to False. args : tuple Additional positional arguments for the prompt function. *kwargs : dict Additional keyword arguments for the prompt function.

Returns

int The integer value entered by the user.

def add_check( self, tag: str, description: str, *args, remember_value=False, **kwargs) -> bool:
323    def add_check(
324        self,
325        tag: str,
326        description: str,
327        *args,
328        remember_value=False,
329        **kwargs,
330    ) -> bool:
331        """
332        @unified
333        Add a yes/no prompt to the GUI.
334
335        Parameters
336        ----------
337        tag : str
338            Tag to identify the widget.
339        description : str
340            The message to display.
341        remember_value : bool, optional
342            Whether to remember the last entered value. Defaults to False.
343        *args : tuple
344            Additional positional arguments for the `prompt` function.
345        **kwargs : dict
346            Additional keyword arguments for the `prompt` function.
347
348        Returns
349        -------
350        bool
351            True if "yes" is selected, False otherwise.
352        """
353        if "default" in kwargs and isinstance(kwargs["default"], bool):
354            kwargs["default"] = "yes" if kwargs["default"] else "no"
355
356        if self.params is not None:
357            if tag in self.params:
358                if self.params[tag]:
359                    kwargs["default"] = "yes"
360                else:
361                    kwargs["default"] = "no"
362        elif remember_value and tag in self.cfg:
363            if self.cfg[tag]:
364                kwargs["default"] = "yes"
365            else:
366                kwargs["default"] = "no"
367
368        value = prompt(  # type: ignore[misc]
369            *args,
370            message=description + " (yes/no): ",
371            completer=WordCompleter(["yes", "no"]),
372            validator=Validator.from_callable(
373                lambda x: x in ["yes", "no"],
374                error_message="Please enter 'yes' or 'no'.",
375                move_cursor_to_end=True,
376            ),
377            **kwargs,
378        )
379        self.cfg[tag] = value.lower() == "yes"
380        self.elements[tag] = Element(self.cfg[tag])
381        return self.elements[tag]

@unified Add a yes/no prompt to the GUI.

Parameters

tag : str Tag to identify the widget. description : str The message to display. remember_value : bool, optional Whether to remember the last entered value. Defaults to False. args : tuple Additional positional arguments for the prompt function. *kwargs : dict Additional keyword arguments for the prompt function.

Returns

bool True if "yes" is selected, False otherwise.

def add_int_text( self, tag: str, description: str = '', *args, remember_value=False, **kwargs) -> int:
383    def add_int_text(
384        self,
385        tag: str,
386        description: str = "",
387        *args,
388        remember_value=False,
389        **kwargs,
390    ) -> int:
391        """
392        @unified
393        Add an integer prompt to the GUI.
394
395        Parameters
396        ----------
397        tag : str
398            Tag to identify the widget.
399        description : str
400            The message to display.
401        remember_value : bool, optional
402            Whether to remember the last entered value. Defaults to False.
403        *args : tuple
404            Additional positional arguments for the `prompt` function.
405        **kwargs : dict
406            Additional keyword arguments for the `prompt` function.
407
408        Returns
409        -------
410        int
411            The integer value entered by the user.
412        """
413        print("ahhhhhhhhh")
414        if "default" in kwargs and isinstance(kwargs["default"], int):
415            kwargs["default"] = str(kwargs["default"])
416
417        if self.params is not None:
418            if tag in self.params:
419                kwargs["default"] = str(self.params[tag])
420        elif remember_value and tag in self.cfg:
421            kwargs["default"] = str(self.cfg[tag])
422        value = prompt(  # type: ignore[misc]
423            *args,
424            message=description + ": ",
425            validator=Validator.from_callable(
426                lambda x: x.isdigit(),
427                error_message="Please enter a valid number.",
428                move_cursor_to_end=True,
429            ),
430            **kwargs,
431        )
432        self.cfg[tag] = int(value)
433        self.elements[tag] = Element(self.cfg[tag])
434        print("ahhhhhhhhh")
435        return self.elements[tag]

@unified Add an integer prompt to the GUI.

Parameters

tag : str Tag to identify the widget. description : str The message to display. remember_value : bool, optional Whether to remember the last entered value. Defaults to False. args : tuple Additional positional arguments for the prompt function. *kwargs : dict Additional keyword arguments for the prompt function.

Returns

int The integer value entered by the user.

def add_bounded_int_text( self, tag: str, description: str, vmin: int, vmax: int, *args, remember_value=False, **kwargs) -> int:
437    def add_bounded_int_text(
438        self,
439        tag: str,
440        description: str,
441        vmin: int,
442        vmax: int,
443        *args,
444        remember_value=False,
445        **kwargs,
446    ) -> int:
447        """
448        @unified
449        Add an integer range prompt to the GUI.
450
451        Parameters
452        ----------
453        tag : str
454            Tag to identify the widget.
455        description : str
456            The message to display.
457        vmin : int
458            Minimum value of the range.
459        vmax : int
460            Maximum value of the range.
461        remember_value : bool, optional
462            Whether to remember the last entered value. Defaults to False.
463        *args : tuple
464            Additional positional arguments for the `prompt` function.
465        **kwargs : dict
466            Additional keyword arguments for the `prompt` function.
467
468        Returns
469        -------
470        int
471            The integer value entered by the user.
472        """
473        if "default" in kwargs and isinstance(kwargs["default"], int):
474            kwargs["default"] = str(kwargs["default"])
475
476        if self.params is not None:
477            if tag in self.params:
478                kwargs["default"] = str(self.params[tag])
479        elif remember_value and tag in self.cfg:
480            kwargs["default"] = str(self.cfg[tag])
481
482        value = prompt(  # type: ignore[misc]
483            *args,
484            message=description + f" ({vmin}-{vmax}): ",
485            validator=Validator.from_callable(
486                lambda x: x.strip() != ""
487                and x.isdigit()
488                and vmin <= int(x) <= vmax,
489                error_message=f"Please enter a valid number ({vmin}-{vmax}).",
490                move_cursor_to_end=True,
491            ),
492            **kwargs,
493        )
494        self.cfg[tag] = int(value)
495        self.elements[tag] = Element(self.cfg[tag])
496        return self.elements[tag]

@unified Add an integer range prompt to the GUI.

Parameters

tag : str Tag to identify the widget. description : str The message to display. vmin : int Minimum value of the range. vmax : int Maximum value of the range. remember_value : bool, optional Whether to remember the last entered value. Defaults to False. args : tuple Additional positional arguments for the prompt function. *kwargs : dict Additional keyword arguments for the prompt function.

Returns

int The integer value entered by the user.

def add_float_text( self, tag: str, description: str = '', *args, remember_value=False, **kwargs) -> float:
498    def add_float_text(
499        self,
500        tag: str,
501        description: str = "",
502        *args,
503        remember_value=False,
504        **kwargs,
505    ) -> float:
506        """
507        @unified
508        Add an integer prompt to the GUI.
509
510        Parameters
511        ----------
512        tag : str
513            Tag to identify the widget.
514        description : str
515            The message to display.
516        remember_value : bool, optional
517            Whether to remember the last entered value. Defaults to False.
518        *args : tuple
519            Additional positional arguments for the `prompt` function.
520        **kwargs : dict
521            Additional keyword arguments for the `prompt` function.
522
523        Returns
524        -------
525        float
526            The float value entered by the user.
527        """
528        if "default" in kwargs and isinstance(kwargs["default"], float):
529            kwargs["default"] = str(kwargs["default"])
530
531        if self.params is not None:
532            if tag in self.params:
533                kwargs["default"] = str(self.params[tag])
534        elif remember_value and tag in self.cfg:
535            kwargs["default"] = str(self.cfg[tag])
536        value = prompt(  # type: ignore[misc]
537            *args,
538            message=description + ": ",
539            validator=Validator.from_callable(
540                lambda x: x.replace(".", "", 1).isdigit(),
541                error_message="Please enter a valid number.",
542                move_cursor_to_end=True,
543            ),
544            **kwargs,
545        )
546        self.cfg[tag] = float(value)
547        self.elements[tag] = Element(self.cfg[tag])
548        return self.elements[tag]

@unified Add an integer prompt to the GUI.

Parameters

tag : str Tag to identify the widget. description : str The message to display. remember_value : bool, optional Whether to remember the last entered value. Defaults to False. args : tuple Additional positional arguments for the prompt function. *kwargs : dict Additional keyword arguments for the prompt function.

Returns

float The float value entered by the user.

def add_bounded_float_text( self, tag: str, description: str, vmin: float, vmax: float, *args, remember_value=False, **kwargs) -> float:
550    def add_bounded_float_text(
551        self,
552        tag: str,
553        description: str,
554        vmin: float,
555        vmax: float,
556        *args,
557        remember_value=False,
558        **kwargs,
559    ) -> float:
560        """
561        @unified
562        Add an integer range prompt to the GUI.
563
564        Parameters
565        ----------
566        tag : str
567            Tag to identify the widget.
568        description : str
569            The message to display.
570        vmin : float
571            Minimum value of the range.
572        vmax : float
573            Maximum value of the range.
574        remember_value : bool, optional
575            Whether to remember the last entered value. Defaults to False.
576        *args : tuple
577            Additional positional arguments for the `prompt` function.
578        **kwargs : dict
579            Additional keyword arguments for the `prompt` function.
580
581        Returns
582        -------
583        float
584            The float value entered by the user.
585        """
586        if "default" in kwargs and isinstance(kwargs["default"], int):
587            kwargs["default"] = str(kwargs["default"])
588
589        if self.params is not None:
590            if tag in self.params:
591                kwargs["default"] = str(self.params[tag])
592        elif remember_value and tag in self.cfg:
593            kwargs["default"] = str(self.cfg[tag])
594
595        value = prompt(  # type: ignore[misc]
596            *args,
597            message=description + f" ({vmin}-{vmax}): ",
598            validator=Validator.from_callable(
599                lambda x: x.strip() != ""
600                and x.replace(".", "", 1).isdigit()
601                and vmin <= float(x) <= vmax,
602                error_message=f"Please enter a valid number ({vmin}-{vmax}).",
603                move_cursor_to_end=True,
604            ),
605            **kwargs,
606        )
607        self.cfg[tag] = float(value)
608        self.elements[tag] = Element(self.cfg[tag])
609        return self.elements[tag]

@unified Add an integer range prompt to the GUI.

Parameters

tag : str Tag to identify the widget. description : str The message to display. vmin : float Minimum value of the range. vmax : float Maximum value of the range. remember_value : bool, optional Whether to remember the last entered value. Defaults to False. args : tuple Additional positional arguments for the prompt function. *kwargs : dict Additional keyword arguments for the prompt function.

Returns

float The float value entered by the user.

def add_dropdown( self, tag: str, options: list, description: str = '', *args, remember_value=False, **kwargs) -> str:
611    def add_dropdown(
612        self,
613        tag: str,
614        options: list,
615        description: str = "",
616        *args,
617        remember_value=False,
618        **kwargs,
619    ) -> str:
620        """
621        @unified
622        Add a dropdown prompt to the GUI.
623
624        Parameters
625        ----------
626        tag : str
627            Tag to identify the widget.
628        description : str
629            The message to display.
630        options : list
631            List of choices for the dropdown.
632        remember_value : bool, optional
633            Whether to remember the last selected value. Defaults to False.
634        *args : tuple
635            Additional positional arguments for the `prompt` function.
636        **kwargs : dict
637            Additional keyword arguments for the `prompt` function.
638
639        Returns
640        -------
641        str
642            The selected choice.
643        """
644        if self.params is not None:
645            if tag in self.params:
646                kwargs["default"] = self.params[tag]
647        elif remember_value and tag in self.cfg:
648            kwargs["default"] = self.cfg[tag]
649
650        value = prompt(  # type: ignore[misc]
651            *args,
652            message=description + ": ",
653            completer=WordCompleter(options),
654            validator=Validator.from_callable(
655                lambda x: x in options,
656                error_message="Please select a valid choice from the dropdown.",
657                move_cursor_to_end=True,
658            ),
659            **kwargs,
660        )
661        self.cfg[tag] = value
662        self.elements[tag] = Element(self.cfg[tag])
663        return self.elements[tag]

@unified Add a dropdown prompt to the GUI.

Parameters

tag : str Tag to identify the widget. description : str The message to display. options : list List of choices for the dropdown. remember_value : bool, optional Whether to remember the last selected value. Defaults to False. args : tuple Additional positional arguments for the prompt function. *kwargs : dict Additional keyword arguments for the prompt function.

Returns

str The selected choice.

def add_path_completer( self, tag: str, description: str, *args, remember_value=False, **kwargs) -> pathlib.Path:
665    def add_path_completer(
666        self, tag: str, description: str, *args, remember_value=False, **kwargs
667    ) -> Path:
668        """
669        @prompt
670        Add a path completer to the GUI.
671
672        Parameters
673        ----------
674        tag : str
675            Tag to identify the widget.
676        description : str
677            The message to display.
678        remember_value : bool, optional
679            Whether to remember the last entered path. Defaults to False.
680        *args : tuple
681            Additional positional arguments for the `prompt` function.
682        **kwargs : dict
683            Additional keyword arguments for the `prompt` function.
684
685        Returns
686        -------
687        Path
688            The path entered by the user.
689        """
690        if self.params is not None:
691            if tag in self.params:
692                kwargs["default"] = str(self.params[tag])
693        elif remember_value and tag in self.cfg:
694            kwargs["default"] = str(self.cfg[tag])
695
696        value = prompt(  # type: ignore[misc]
697            *args,
698            message=description + ": ",
699            completer=PathCompleter(),
700            validator=Validator.from_callable(
701                lambda x: Path(x).exists(),
702                error_message="Please enter a valid path.",
703                move_cursor_to_end=True,
704            ),
705            **kwargs,
706        )
707        self.cfg[tag] = Path(value)
708        self.elements[tag] = Element(self.cfg[tag])
709        return self.elements[tag]

@prompt Add a path completer to the GUI.

Parameters

tag : str Tag to identify the widget. description : str The message to display. remember_value : bool, optional Whether to remember the last entered path. Defaults to False. args : tuple Additional positional arguments for the prompt function. *kwargs : dict Additional keyword arguments for the prompt function.

Returns

Path The path entered by the user.

def add_output(self, tag: str, *args, **kwargs):
711    def add_output(self, tag: str, *args, **kwargs):
712        """
713        @unified
714        Does nothing in the terminal-based GUI.
715
716        Parameters
717        ----------
718        tag : str
719            Tag to identify the widget.
720        *args : tuple
721            Additional positional arguments for the widget.
722        **kwargs : dict
723            Additional keyword arguments for the widget.
724        """
725        pass

@unified Does nothing in the terminal-based GUI.

Parameters

tag : str Tag to identify the widget. args : tuple Additional positional arguments for the widget. *kwargs : dict Additional keyword arguments for the widget.

def clear_elements(self):
727    def clear_elements(self):
728        """
729        @unified
730        Clear all elements from the GUI.
731        """
732        self.elements = {}

@unified Clear all elements from the GUI.

def save_parameters(self, path: str):
734    def save_parameters(self, path: str):
735        """
736        @unified
737        Save the widget values to a file.
738
739        Parameters
740        ----------
741        path : str
742            The path to save the file.
743        """
744        if not path.endswith(".yml"):
745            path += f"{self.title}_parameters.yml"
746        out = {}
747        for tag in self.elements:
748            if tag.startswith("label_"):
749                pass
750            elif hasattr(self.elements[tag], "value"):
751                out[tag] = self.elements[tag].value
752        with open(path, "w") as f:
753            yaml.dump(out, f)

@unified Save the widget values to a file.

Parameters

path : str The path to save the file.

def save_settings(self):
755    def save_settings(self):
756        """
757        @unified
758        Save the widget values to the configuration file.
759        """
760        for tag in self.elements:
761            if tag.startswith("label_"):
762                pass
763            elif hasattr(self.elements[tag], "value"):
764                self.cfg[tag] = self.elements[tag].value
765        config_file = CONFIG_PATH / f"{self.title}.yml"
766        config_file.parent.mkdir(exist_ok=True)
767
768        base_config = self._get_config(self.title)  # loads the config file
769        for key, value in self.cfg.items():
770            base_config[key] = value
771
772        with open(config_file, "w") as f:
773            yaml.dump(base_config, f)

@unified Save the widget values to the configuration file.

def show(self):
775    def show(self):
776        """
777        @unified
778        Display the GUI. (No-op for terminal-based GUIs.)
779        """
780        pass

@unified Display the GUI. (No-op for terminal-based GUIs.)