Last active
December 24, 2024 20:33
-
-
Save Pinacolada64/d837151e1aec80d859872b297437e9ca to your computer and use it in GitHub Desktop.
A better attempt at terminal settings
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import logging | |
import textwrap | |
from dataclasses import asdict, dataclass, is_dataclass, field | |
from enum import Enum | |
""" | |
Goal: to display a settings menu. The way I think it should work: | |
- Enumerate through Player atributes, get a key (LINE_ENDING) | |
- Get a key name common to both SettingString.LINE_ENDING (to display in the menu item) and SettingValue.LINE_ENDING | |
- Use that as a lookup in the Enum list? | |
""" | |
class KeyboardKeyName(str, Enum): | |
RETURN = "Return" | |
ENTER = "Enter" | |
def __str__(self): | |
return self.value | |
class TerminalSettingString(str, Enum): | |
# these are terminal settings strings to be displayed in menu | |
NAME = "Name" | |
RETURN_KEY = "Return or Enter key?" | |
TRANSLATION = "Character translation" | |
LINE_ENDING = "Line ending(s)" | |
SCREEN_ROWS = "Screen rows" | |
SCREEN_COLS = "Screen columns" | |
def __str__(self): | |
# return e.g., "Line ending(s)" | |
return self.value | |
def __repr__(self): | |
# return e.g., "LINE_ENDING" | |
return self.name | |
@dataclass | |
class Translation: | |
ASCII = "ASCII" | |
ANSI = "ANSI" | |
PETSCII = "PetSCII" | |
def __str__(self): | |
return self.value | |
@dataclass(frozen=True) | |
class LineEnding: | |
name: str | |
ending: str | |
def __repr__(self): | |
return self.name | |
def __str__(self): | |
return self.ending | |
@dataclass(frozen=True) | |
class ReturnKey: | |
name: str | |
def __str__(self): | |
return KeyboardKeyName.value | |
CRLineEnding = LineEnding(name="Carriage return", ending="\r") | |
LFLineEnding = LineEnding(name="Line feed", ending="\n") | |
CRLFLineEnding = LineEnding(name="Carriage return + line feed", ending="\r\n") | |
@dataclass | |
class UserSetting: | |
NAME: str | None = None | |
RETURN_KEY: KeyboardKeyName = field(default_factory=lambda: KeyboardKeyName.ENTER) | |
TRANSLATION: Translation = field(default_factory=lambda: Translation.ANSI) | |
LINE_ENDING: LineEnding = field(default_factory=lambda: CRLFLineEnding) | |
SCREEN_COLS: int = 40 | |
SCREEN_ROWS: int = 25 | |
@dataclass | |
class Player(object): | |
name: str | |
# copy UserSetting class here: | |
settings: UserSetting = field(default_factory=lambda: UserSetting()) | |
def show_terminal_settings(self): | |
""" | |
e.g., | |
TerminalSettingString....: Player.UserSetting | |
1. Name.....................: Generic Commodore client | |
2. Return or Enter key?.....: Return | |
3. Character translation....: PetSCII | |
4. Line ending(s)...........: Carriage return + line feed | |
5. Screen columns...........: 40 | |
6. Screen rows..............: 25 | |
""" | |
for k, v in enumerate(asdict(self.settings)): | |
value = getattr(self.settings, v) | |
value_str = repr(value) if is_dataclass(value) else value | |
print( | |
f"{k + 1}. {TerminalSettingString[v].value.ljust(25, '.')}: " | |
f"{value_str}" | |
) | |
def edit_settings(self): | |
max_options = len(TerminalSettingString) | |
log.debug("max_options: %i" % max_options) | |
self.show_terminal_settings() | |
while True: | |
choice = input(f"Edit which [1-{max_options}, Q)uit]: ") | |
if choice.lower()[0] == "q": | |
break | |
try: | |
option_number = int(choice) | |
# Get the TerminalSettingString enum member | |
setting_enum = list(TerminalSettingString)[option_number - 1] | |
# Determine the type of setting and get the corresponding Enum | |
setting_enums_dict = { | |
TerminalSettingString.TRANSLATION: (lambda: t for t in Translation.), | |
TerminalSettingString.LINE_ENDING: lambda: l for l in LineEnding, | |
# FIXME: finish this | |
} | |
allowed_values = list(setting_enums_dict.get(setting_enum, None)) | |
logging.debug(allowed_values) | |
if allowed_values: | |
# Present the user with a list of valid choices for the Enum members: | |
print(f"Choose a value for {setting_enum.value}:") | |
for i, value in enumerate(allowed_values, start=1): | |
print(f"{i}. {value.value}") | |
# Get user input for the chosen value | |
while True: | |
try: | |
choice = int(input("Enter your choice: ")) | |
if 1 <= choice <= len(allowed_values): | |
selected_value = allowed_values(choice) # Convert choice to Enum member | |
break | |
else: | |
print(f"Invalid choice. Please enter a number between 1 and {len(allowed_values)}.") | |
except ValueError: | |
print("Please enter a number.") | |
# Update the player's setting | |
setattr(self.settings, setting_enum.name, selected_value) | |
else: | |
# Handle settings without a predefined Enum (e.g., Name) | |
print(f"Setting '{setting_enum.value}' is not editable.") | |
except ValueError: | |
print("FIXME: Bad value.") | |
def output(self, s: str) -> None: | |
# print(s, end=str(self.settings.LINE_ENDING)) | |
print(textwrap.fill(text=s, width=self.settings.SCREEN_COLS), end=self.settings.LINE_ENDING.ending) | |
if __name__ == "__main__": | |
log = logging.getLogger(__name__) | |
print(log) | |
logging.basicConfig(level=logging.DEBUG, | |
# format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', | |
# https://stackoverflow.com/questions/10973362/python-logging-function-name-file-name-line-number-using-a-single-file | |
format = "[%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s", | |
datefmt='%m-%d %H:%M') | |
log.setLevel(logging.DEBUG) | |
log.info("Running") # displays filename, line number, function name | |
# much better code by volca! | |
player = Player(name="Ryan", settings=UserSetting(NAME="Generic Commodore client", | |
TRANSLATION=Translation.ASCII, | |
LINE_ENDING=CRLFLineEnding, | |
RETURN_KEY=KeyboardKeyName.RETURN)) | |
# player.show_terminal_settings() | |
player.edit_settings() | |
player.output("Demonstrating word-wrap:") | |
player.output("You probably want to figure out how to print with the user's CR/LF preferences without needing " | |
"access to the player object.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment