the-honk/school/a-level/Y12 2022-2024/Algorithms/bitwise/applyCurses.py

141 lines
4.7 KiB
Python
Raw Normal View History

2024-10-09 17:02:48 +00:00
# windows: pip install windows-curses
import curses
from operator import and_, or_, xor
def initCurses() -> tuple:
"""Initialise curses and any necessary colour pairs"""
screen = curses.initscr()
screen.keypad(True)
curses.cbreak()
curses.noecho()
curses.start_color()
# default
curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK)
default = curses.color_pair(1)
# highlighted
curses.init_pair(2, curses.COLOR_BLACK, curses.COLOR_WHITE)
highlighted = curses.color_pair(2)
# error
curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK)
error = curses.color_pair(3)
return (screen, (default, highlighted, error))
def destroyCurses(screen):
"""Close out of curses and reverse any destructive changes to the terminal"""
curses.nocbreak()
screen.keypad(False)
curses.echo()
curses.endwin()
def selectMenu(screen, options: list, header: str = "Pick an option:"):
selectedIndex = 0
optionCount = len(options)
while True:
screen.clear()
screen.addstr(f"{header}\n\n", curses.A_BOLD)
for i in range(optionCount):
screen.addstr(f"{i + 1}. ")
screen.addstr(f"{options[i]}\n", highlighted if i == selectedIndex else default)
c = screen.getch()
if c == curses.KEY_UP or c == curses.KEY_LEFT:
selectedIndex -= 1 - optionCount
selectedIndex %= optionCount
elif c == curses.KEY_DOWN or c == curses.KEY_RIGHT:
selectedIndex += 1
selectedIndex %= optionCount
elif c == curses.KEY_ENTER or chr(c) in '\n\r':
return selectedIndex + 1
def getBinary(screen, prompt: str, length: int = None) -> list:
errorMessage = None
while True:
screen.clear()
screen.addstr(0, 0, f"{prompt}", curses.A_BOLD)
# display an error message if necessary
if errorMessage:
screen.addstr(3, 0, errorMessage, error | curses.A_BOLD)
errorMessage = None
# fetch the inputted data
curses.echo()
bits = list(screen.getstr(1, 0, 50).decode("utf-8"))
curses.noecho()
# validate that a binary value was entered
for bit in bits:
if bit not in ["0", "1"]:
errorMessage = "Invalid binary number. Please try again."
if len(bits) == 0:
errorMessage = "Please make sure you enter a value."
elif length and len(bits) != length:
errorMessage = f"Please make sure the value is {length} bits long."
if errorMessage:
continue
return [int(x) for x in bits]
def applyOperator(screen, binary: list, mask: list) -> list:
# todo: fix
# operatorList = list(OPERATORS.keys())
# symbol, operator = OPERATORS[operatorList[selectMenu(screen, operatorList) - 1]]
# output = "".join(str(operator(a, b)) for a, b in zip(binary, mask))
binary = "".join(str(x) for x in binary)
mask = "".join(str(x) for x in mask)
print(binary, mask, screen)
while True:
screen.clear()
screen.addstr(0, 0, f"{binary} {mask}", curses.A_BOLD)
screen.addstr(3, 0, "Press the backspace key to get back to the main menu.")
screen.addstr(4, 0, "")
# get pressed key
c = screen.getch()
if c == 8 or c == 127 or c == curses.KEY_BACKSPACE:
break
OPERATORS = {
'AND': ('&', lambda binary, mask: applyOperator(binary, mask, and_)),
'OR': ('|', lambda binary, mask: applyOperator(binary, mask, or_)),
'XOR': ('^', lambda binary, mask: applyOperator(binary, mask, xor))
}
if __name__ == "__main__":
# init curses
screen, (default, highlighted, error) = initCurses()
binary, mask = [], []
while True:
binaryStr = "[NONE]" if len(binary) == 0 else "".join(str(x) for x in binary)
maskStr = "[NONE]" if len(mask) == 0 else "".join(str(x) for x in mask)
choice = selectMenu(screen, ["Apply a gate", "Change the binary number", "Change the mask", "Exit"], f"Current binary number: {binaryStr}\nCurrent mask: {maskStr}\n\nWhat would you like to do?")
if choice == 1:
applyOperator(screen, binary, mask)
elif choice == 2:
binary = getBinary(screen, "Please enter a new binary value:", None if len(mask) == 0 else len)
elif choice == 3:
mask = getBinary(screen, "Please enter a new mask:", None if len(binary) == 0 else len(binary))
elif choice == 4:
break
# destroy curses
destroyCurses(screen)