1.0.40 (Increased limits on the menu function) and 1.0.41 (Windows "Support")

This commit is contained in:
Firepup Sixfifty 2024-08-30 12:15:36 -05:00
parent 969446bed6
commit 1f13093f38
Signed by: Firepup650
SSH key fingerprint: SHA256:cb8sEJwc0kQJ6/nMUhscWRe35itf0NFMdSKl3v4qt48
3 changed files with 230 additions and 169 deletions

View file

@ -1,7 +1,9 @@
# Firepup650 # Firepup650
Package containing various shorthand things I use, and a few imports I almost always use Package containing various shorthand things I use, and a few imports I almost always use
### Change log: ### Change log:
#### v.1.0.39: #### v.1.0.41:
Windows "Support"
#### v.1.0.40:
Add offset mapping all the way up to 10 Billion, which exceeds the integer limit. Add offset mapping all the way up to 10 Billion, which exceeds the integer limit.
#### v.1.0.39: #### v.1.0.39:
Add offset mappings for exceeding 1 Million options, new limit is 10 Million options Add offset mappings for exceeding 1 Million options, new limit is 10 Million options

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "firepup650" name = "firepup650"
version = "1.0.39" version = "1.0.41"
authors = ["Firepup650 <firepyp650@gmail.com>"] authors = ["Firepup650 <firepyp650@gmail.com>"]
description = "Package containing various shorthand things I use, and a few imports I almost always use" description = "Package containing various shorthand things I use, and a few imports I almost always use"
readme = "README.md" readme = "README.md"

View file

@ -1,10 +1,19 @@
"""Firepup650's PYPI Package""" """Firepup650's PYPI Package"""
import os, sys, termios, tty, time, sqlite3, ast, pydoc # type: ignore[import] fkey, termios, tty = None, None, None
import random as r
import fkeycapture as fkey
import fpsql as fql
from warnings import warn as ww from warnings import warn as ww
try:
import termios, tty, fkeycapture as fkey
except ImportError:
ww(
"Warning! This module has reduced functionality on Windows! I hope you know what you're doing!",
stackLevel=2,
)
import os, sys, time, sqlite3, ast, pydoc # type: ignore[import]
import random as r
import fpsql as fql
from typing import NoReturn, TypeVar, Type, Optional, List, Any, Union from typing import NoReturn, TypeVar, Type, Optional, List, Any, Union
from collections.abc import Iterable from collections.abc import Iterable
@ -32,11 +41,15 @@ def alias(func):
return decorator return decorator
__VERSION__ = "1.0.34" __VERSION__ = "1.0.41"
__NEW__ = "Adds methods to hide/show the cursor and a menu system" __NEW__ = 'Windows "Support"'
__LICENSE__ = "MIT" __LICENSE__ = "MIT"
class NotImplementedOnWindowsException(NotImplementedException):
"""Exception raised when a Linux only method is called on a Windows machine"""
def flushPrint(*args) -> None: def flushPrint(*args) -> None:
"""# Function: flushPrint """# Function: flushPrint
Prints and flushes the provided args. Prints and flushes the provided args.
@ -124,38 +137,52 @@ def gp(
allowDelete: bool = False, allowDelete: bool = False,
filler: str = "-", filler: str = "-",
) -> Union[str, bytes]: ) -> Union[str, bytes]:
"""# Function: gp raise NotImplementedOnWindowsException(
Get keys and print them. "This method is not implemented for Windows machines"
# Inputs: )
keycount: int - Number of keys to get, defaults to 1
chars: list - List of keys to accept, defaults to ["1", "2"]
bytes: bool - Wether to return the kyes as bytes, defaults to False
allowDelete: bool - Wether to allow deleting chars, defaults to False
filler: str - The character to use as filler when waiting on more chars, defaults to "-"
# Returns:
Union[str, bytes] - Keys pressed
# Raises: if fkey:
None"""
got = 0 def gp(
keys = [] keycount: int = 1,
if allowDelete: chars: list = ["1", "2"],
chars.append(fkey.KEYS["BACKSPACE"].decode()) bytes: bool = False,
flushPrint(filler * keycount) allowDelete: bool = False,
while len(keys) < keycount: filler: str = "-",
key = fkey.getchars(1, chars, True) # type: bytes #type: ignore ) -> Union[str, bytes]:
if not allowDelete or key != fkey.KEYS["BACKSPACE"]: """# Function: gp
keys.append(key.decode()) Get keys and print them.
elif len(keys): # Inputs:
keys.pop() keycount: int - Number of keys to get, defaults to 1
flushPrint(f"\033[{keycount}D{''.join(keys)}{filler*(keycount-len(keys))}") chars: list - List of keys to accept, defaults to ["1", "2"]
got += 1 bytes: bool - Wether to return the kyes as bytes, defaults to False
print() allowDelete: bool - Wether to allow deleting chars, defaults to False
if not bytes: filler: str - The character to use as filler when waiting on more chars, defaults to "-"
return "".join(keys)
else: # Returns:
return ("".join(keys)).encode() Union[str, bytes] - Keys pressed
# Raises:
None"""
got = 0
keys = []
if allowDelete:
chars.append(fkey.KEYS["BACKSPACE"].decode())
flushPrint(filler * keycount)
while len(keys) < keycount:
key = fkey.getchars(1, chars, True) # type: bytes #type: ignore
if not allowDelete or key != fkey.KEYS["BACKSPACE"]:
keys.append(key.decode())
elif len(keys):
keys.pop()
flushPrint(f"\033[{keycount}D{''.join(keys)}{filler*(keycount-len(keys))}")
got += 1
print()
if not bytes:
return "".join(keys)
else:
return ("".join(keys)).encode()
def gh( def gh(
@ -166,65 +193,88 @@ def gh(
allowDelete: bool = False, allowDelete: bool = False,
filler: str = "-", filler: str = "-",
) -> Union[str, bytes]: ) -> Union[str, bytes]:
"""# Function: gh raise NotImplementedOnWindowsException(
Get keys and print `char` in their place. "This method is not implemented for Windows machines"
# Inputs: )
keycount: int - Number of keys to get, defaults to 1
chars: list - List of keys to accept, defaults to ["1", "2"]
char: str - Character to use to obfuscate the keys, defaults to *
bytes: bool - Wether to return the kyes as bytes, defaults to False
allowDelete: bool - Wether to allow deleting chars, defaults to False
filler: str - The character to use as filler when waiting on more chars, defaults to "-"
# Returns:
Union[str, bytes] - Keys pressed
# Raises: if fkey:
None"""
got = 0 def gh(
keys = [] keycount: int = 1,
if allowDelete: chars: list = ["1", "2"],
chars.append(fkey.KEYS["BACKSPACE"].decode()) char: str = "*",
flushPrint(filler * keycount) bytes: bool = False,
while len(keys) < keycount: allowDelete: bool = False,
key = fkey.getchars(1, chars, True) # type: bytes #type: ignore filler: str = "-",
if not allowDelete or key != fkey.KEYS["BACKSPACE"]: ) -> Union[str, bytes]:
keys.append(key.decode()) """# Function: gh
elif len(keys): Get keys and print `char` in their place.
keys.pop() # Inputs:
flushPrint(f"\033[{keycount}D{char*len(keys)}{filler*(keycount-len(keys))}") keycount: int - Number of keys to get, defaults to 1
got += 1 chars: list - List of keys to accept, defaults to ["1", "2"]
print() char: str - Character to use to obfuscate the keys, defaults to *
if not bytes: bytes: bool - Wether to return the kyes as bytes, defaults to False
return "".join(keys) allowDelete: bool - Wether to allow deleting chars, defaults to False
else: filler: str - The character to use as filler when waiting on more chars, defaults to "-"
return ("".join(keys)).encode()
# Returns:
Union[str, bytes] - Keys pressed
# Raises:
None"""
got = 0
keys = []
if allowDelete:
chars.append(fkey.KEYS["BACKSPACE"].decode())
flushPrint(filler * keycount)
while len(keys) < keycount:
key = fkey.getchars(1, chars, True) # type: bytes #type: ignore
if not allowDelete or key != fkey.KEYS["BACKSPACE"]:
keys.append(key.decode())
elif len(keys):
keys.pop()
flushPrint(f"\033[{keycount}D{char*len(keys)}{filler*(keycount-len(keys))}")
got += 1
print()
if not bytes:
return "".join(keys)
else:
return ("".join(keys)).encode()
def printt(text: str, delay: float = 0.1, newline: bool = True) -> None: def printt(text: str, delay: float = 0.1, newline: bool = True) -> None:
"""# Function: printt raise NotImplementedOnWindowsException(
Print out animated text! "This method is not implemented for Windows machines"
# Inputs: )
text: str - Text to print (could technicaly be a list)
delay: float - How long to delay between characters, defaults to 0.1
newline: bool - Wether or not to add a newline at the end of the text, defaults to True
# Returns:
None
# Raises: if fkey:
None"""
# Store the current terminal settings def printt(text: str, delay: float = 0.1, newline: bool = True) -> None:
original_terminal_settings = termios.tcgetattr(sys.stdin) """# Function: printt
# Change terminal settings to prevent any interruptions Print out animated text!
tty.setcbreak(sys.stdin) # Inputs:
for char in text: text: str - Text to print (could technicaly be a list)
flushPrint(char) delay: float - How long to delay between characters, defaults to 0.1
time.sleep(delay) newline: bool - Wether or not to add a newline at the end of the text, defaults to True
if newline:
print() # Returns:
# Restore the original terminal settings None
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, original_terminal_settings)
# Raises:
None"""
# Store the current terminal settings
original_terminal_settings = termios.tcgetattr(sys.stdin)
# Change terminal settings to prevent any interruptions
tty.setcbreak(sys.stdin)
for char in text:
flushPrint(char)
time.sleep(delay)
if newline:
print()
# Restore the original terminal settings
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, original_terminal_settings)
@alias(time.sleep) @alias(time.sleep)
@ -633,11 +683,12 @@ class console:
# Raises: # Raises:
None""" None"""
ind = 1 ind = 1
while warning in console.__warnings__: warn = warning
warning = f"{warning}({ind})" while warn in console.__warnings__:
warn = f"{warning}({ind})"
ind += 1 ind += 1
console.__warnings__.append(warning) console.__warnings__.append(warn)
ww(warning, class_, 2) ww(warn, class_, 2)
@staticmethod @staticmethod
def error(*args, **kwargs) -> None: def error(*args, **kwargs) -> None:
@ -855,81 +906,89 @@ def hidden(func):
return wrapper return wrapper
@hidden
def menu(options: dict, title: str = "") -> object: def menu(options: dict, title: str = "") -> object:
"""# Function: menu raise NotImplementedOnWindowsException(
Uses a nice interactive for the provided options "This method is not implemented for Windows machines"
# Inputs:
options: dict - A dictionary of options and their return values
# Returns:
object - The user's selected option
# Raises:
None"""
if type(options) != dict:
raise ValueError(f"options must be a dictionary (passed a {type(options)})")
if len(options) <= 1:
raise ValueError(
f"options must contain at least two choices (passed {len(options)})"
)
choices = list(options)
limit = len(choices)
current = 0
selected = False
UP = [fkey.KEYS["UP"], b"w", b"a", fkey.KEYS["LEFT"]]
DOWN = [fkey.KEYS["DOWN"], b"s", b"d", fkey.KEYS["RIGHT"]]
indicatorSize = len(str(limit)) * 2 + 1
indicatorOffset = 999
match indicatorSize:
case 3: # 1-9 options (Ten rolls over)
indicatorOffset = 1
case 5: # 10-99 options (One Hundered rolls over)
indicatorOffset = 0
case 7: # 100-999 options (One Thousand rolls over)
indicatorOffset = -1
case 9: # 1000-9999 options (Ten Thousand rolls over)
indicatorOffset = -2
case 11: # 10000-99999 options (One Hundred Thousand rolls over)
indicatorOffset = -3
case 13: # 100000-999999 options (One Million rolls over)
indicatorOffset = -4
case 15: # 1000000-9999999 options (Ten Million rolls over)
indicatorOffset = -5
case 17: # 10000000-99999999 options (One Hundred Million rolls over)
indicatorOffset = -6
case 19: # 100000000-999999999 options (One Billion rolls over)
indicatorOffset = -7
case (
21
): # 1000000000=9999999999 options (Ten Billion rolls over) (This exceeds integer limits, so if we get over this I've got no clue how.)
indicatorOffset = -8
case _:
raise ValueError(
f"You have more menu options than was ever expected to be used, please notify the package author to add a offset mappting for an indicator size of {indicatorSize}."
)
menuWidth = max(
[max([len(choice) for choice in choices]) + 4, indicatorSize * 2 + 7]
) )
while not selected:
clear()
flushPrint( if fkey:
(title + "\n" if title else "")
+ f"{''*menuWidth}\n" @hidden
+ f"{f'{current+1}'}{' '*(len(str(limit))-len(str(current+1)))}/{limit}{' '*int(menuWidth/2-indicatorSize-2.5)}{' '*int((menuWidth-indicatorSize)/2-indicatorOffset+(1 if menuWidth%2==0 else 0))}\n" def menu(options: dict, title: str = "") -> object:
+ f"║←{' '*int(((menuWidth-len(choices[current]))/2)-1)}{choices[current]}{' '*int((menuWidth-len(choices[current]))/2-.5)}→║\n" """# Function: menu
+ f"{' '*int((menuWidth-1)/2)}{' '*int((menuWidth-1)/2+.5)}\n" Uses a nice interactive for the provided options
+ f"{''*menuWidth}\n" # Inputs:
options: dict - A dictionary of options and their return values
# Returns:
object - The user's selected option
# Raises:
None"""
if type(options) != dict:
raise ValueError(f"options must be a dictionary (passed a {type(options)})")
if len(options) <= 1:
raise ValueError(
f"options must contain at least two choices (passed {len(options)})"
)
choices = list(options)
limit = len(choices)
current = 0
selected = False
UP = [fkey.KEYS["UP"], b"w", b"a", fkey.KEYS["LEFT"]]
DOWN = [fkey.KEYS["DOWN"], b"s", b"d", fkey.KEYS["RIGHT"]]
indicatorSize = len(str(limit)) * 2 + 1
indicatorOffset = 999
match indicatorSize:
case 3: # 1-9 options (Ten rolls over)
indicatorOffset = 1
case 5: # 10-99 options (One Hundered rolls over)
indicatorOffset = 0
case 7: # 100-999 options (One Thousand rolls over)
indicatorOffset = -1
case 9: # 1000-9999 options (Ten Thousand rolls over)
indicatorOffset = -2
case 11: # 10000-99999 options (One Hundred Thousand rolls over)
indicatorOffset = -3
case 13: # 100000-999999 options (One Million rolls over)
indicatorOffset = -4
case 15: # 1000000-9999999 options (Ten Million rolls over)
indicatorOffset = -5
case 17: # 10000000-99999999 options (One Hundred Million rolls over)
indicatorOffset = -6
case 19: # 100000000-999999999 options (One Billion rolls over)
indicatorOffset = -7
case (
21
): # 1000000000-9999999999 options (Ten Billion rolls over) (This exceeds integer limits, so if we get over this I've got no clue how.)
indicatorOffset = -8
case _:
raise ValueError(
f"You have more menu options than was ever expected to be used, please notify the package author to add a offset mappting for an indicator size of {indicatorSize}."
)
menuWidth = max(
[max([len(choice) for choice in choices]) + 4, indicatorSize * 2 + 7]
) )
key = fkey.get(bytes=True, osReader=True) while not selected:
if key in UP: clear()
current -= 1 flushPrint(
elif key in DOWN: (title + "\n" if title else "")
current += 1 + f"{''*menuWidth}\n"
elif key in [fkey.KEYS["ENTER"]]: + f"{f'{current+1}'}{' '*(len(str(limit))-len(str(current+1)))}/{limit}{' '*int(menuWidth/2-indicatorSize-2.5)}{' '*int((menuWidth-indicatorSize)/2-indicatorOffset+(1 if menuWidth%2==0 else 0))}\n"
break + f"║←{' '*int(((menuWidth-len(choices[current]))/2)-1)}{choices[current]}{' '*int((menuWidth-len(choices[current]))/2-.5)}→║\n"
if current > limit - 1: + f"{' '*int((menuWidth-1)/2)}{' '*int((menuWidth-1)/2+.5)}\n"
current = 0 + f"{''*menuWidth}\n"
if current < 0: )
current = limit - 1 key = fkey.get(bytes=True, osReader=True)
return options[choices[current]] if key in UP:
current -= 1
elif key in DOWN:
current += 1
elif key in [fkey.KEYS["ENTER"]]:
break
if current > limit - 1:
current = 0
if current < 0:
current = limit - 1
return options[choices[current]]