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)
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.