Introduction
A while back, I wrote a script in Python that allowed me to essentially use a keybind to pop open a menu to do various things I usually did in the command-line, such as starting certain local servers or scraping Flarum forums. I called it the Toolbox, and the name has stuck with it ever since.
However, the main problem with the script was that it was extremely limited in what it could do, and its code was basically all over the place. Here's the source code so you can see what I mean:
import termcolor
import colorama
from colorama import Fore, Back, Style
import os
import subprocess
from pyfiglet import figlet_format
#--
import Forum
import LyicxScript
#--
colorama.init()
#--
#os.system("title Toolbox v1.3")
#--
multiMC_directory = "E:\\Documents\\MultiMC\\"
ftc_directory = "E:\\Toolbox\\Telnet\\"
def MainMenu(error = None):
os.system("cls")
print(termcolor.colored(figlet_format("Toolbox", font = "small"), 'blue')
+ termcolor.colored("What would you like to do?\n", "cyan"))
options = [
"Start the local Minecraft development test server",
"Start the local Minecraft experimental server",
"Scrape a Flarum forum",
"Launch Minecraft (Carbon Fiber)",
"Launch Minecraft (Blackwidow)",
"Launch FreedomTelnetClient+",
"Generate smithing concrete colors",
"Generate smithing concrete colors (Looped)",
"Open the All Tasks folder",
"Nothing (Exit)"
]
if error:
print(termcolor.colored(error + "\n", "red"))
for num in range(len(options)):
text = termcolor.colored("[" + str(num + 1) + "] ", "cyan") + termcolor.colored(options[num], "cyan")
print(text)
promptChoice()
def promptChoice():
choice = input("\nChoice: ")
mode = {
1: fireUpTestServer,
2: fireUpExperimentalServer,
3: fireUpTP,
4: fireUpCarbonFiber,
5: fireUpBlackwidow,
6: fireUpTelnet,
7: runColors,
8: runColorsLooped,
11: openGodMode,
12: exit
}
if not choice or not choice.isnumeric() or int(choice) not in mode.keys():
MainMenu("That isn't an option.")
return None
return mode.get(int(choice), MainMenu)()
def openGodMode():
print(termcolor.colored("\n" + "Opening the All Tasks folder...", "cyan"))
os.system("explorer.exe shell:::{ED7BA470-8E54-465E-825C-99712043E01C}")
def fireUpTestServer():
os.chdir("E:\Documents\ouch")
subprocess.call(["java", "-jar", "paper-1.16.5-777.jar", "-nogui"])
def fireUpExperimentalServer():
os.chdir("E:\Documents\Experimental Server")
subprocess.call(["java", "-jar", "paper-1.16.5-777.jar", "-nogui"])
def fireUpCarbonFiber():
os.chdir(multiMC_directory)
subprocess.call(["MultiMC.exe", "--launch", "Carbon Fiber", "--profile", "videogamesm12"])
def fireUpBlackwidow():
os.chdir(multiMC_directory)
subprocess.call(["MultiMC.exe", "--launch", "Blackwidow", "--profile", "MojangCantCode"])
def fireUpTelnet():
os.chdir(ftc_directory)
subprocess.call(["FreedomTelnetClientPlus-2.1-INDEV-032821-202733.exe"])
def runColors():
os.chdir("E:\Toolbox\Concretes")
os.system("cls")
LyicxScript.init()
def runColorsLooped():
os.chdir("E:\Toolbox\Concretes")
os.system("cls")
LyicxScript.init(True)
def fireUpTP():
working_directory = "E:\Toolbox\Forum"
os.system("cls")
os.system("title Titan v" + Forum.version)
print(Fore.BLUE + figlet_format("Titan", font = "small")
+ Fore.CYAN + Style.DIM + "Using " + Fore.BLUE + working_directory + Fore.CYAN + Style.DIM + " as the working directory.\n")
#directory_prompt = input(Fore.RED + "Location of archived threads: ")
#if directory_prompt:
# working_directory = directory_prompt
os.chdir(working_directory)
print(Fore.BLUE + Style.DIM + "### BASICS ###\n"
+ Fore.CYAN + Style.DIM + "At the moment, you don't have to enter anything other than the forum you're trying to scrape."
+ "\n")
Forum.promptForum()
if __name__ == "__main__":
MainMenu()
Display More
Rewrite
To make things feel more graphical and allow for a more flexible interface, I recently rewrote the script to essentially make it feel more graphical and in doing so, built the foundation for a potentially useful graphical framework that lets you make simple menus using the curses library. So, I've decided to release the code for the Framework (the backend of the now-rewritten Toolbox) today. There are some places where it is rough around the edges but one of these days I'll get around to fixing it. For now, enjoy.
import curses
import pyfiglet
class Option:
def __init__(self, label, action, args = []):
self.label = label
self.action = action
self.args = args
self.selected = False
def setSelected(self, value):
self.selected = value
def doAction(self):
self.action(self.args)
class Menu:
def __init__(self, title, text):
self.title = title
self.text = text
self.options = []
def addOption(self, option):
self.options.append(option)
def addAllOptions(self, options):
for option in options:
self.options.append(option)
def open(self, stdscr):
k = 0
selection = 0
offset = 0
stdscr.clear()
stdscr.refresh()
curses.start_color()
curses.init_pair(1, curses.COLOR_BLUE, curses.COLOR_BLACK)
curses.init_pair(2, curses.COLOR_CYAN, curses.COLOR_BLACK)
curses.init_pair(3, curses.COLOR_BLACK, curses.COLOR_WHITE)
curses.curs_set(False)
while True:
stdscr.clear()
height, width = stdscr.getmaxyx()
# Key event handling
if k == curses.KEY_DOWN:
if selection + 1 < 0:
pass
elif selection + 1 > len(self.options) - 1:
pass
else:
selection = selection + 1
self.options[selection - 1].selected = False
if (((len(self.options) + 5) - 2) > height - 5):
offset = offset + 1
elif k == curses.KEY_UP:
if selection - 1 < 0:
pass
elif selection - 1 > len(self.options) + 1:
pass
else:
selection = selection - 1
self.options[selection + 1].selected = False
if (((len(self.options) + 5) - 2) > height - 5):
offset = offset - 1
elif k == curses.KEY_ENTER or k == 10 or k == 13:
curses.endwin()
self.options[selection].doAction()
return
self.options[selection].selected = True
# Title setup
title = pyfiglet.figlet_format(self.title, font = "small").split("\n")
title = title[:-1]
statusbarstr = " {} | Current selection: {}".format((" ", "\u2191")[offset > 0], selection + 1)
# Render status bar
stdscr.attron(curses.color_pair(3))
stdscr.addstr(height-1, 0, statusbarstr)
stdscr.addstr(height-1, len(statusbarstr), " " * (width - len(statusbarstr) - 1))
stdscr.attroff(curses.color_pair(3))
# Turning on attributes for title
stdscr.attron(curses.color_pair(1))
# stdscr.attron(curses.A_BOLD)
# Line
linenum = 0
# Rendering title
for line in title:
stdscr.addstr(linenum, 0, line)
linenum = linenum + 1
stdscr.attroff(curses.color_pair(1))
# stdscr.attroff(curses.A_BOLD)
stdscr.attron(curses.color_pair(2))
stdscr.addstr(linenum, 0, self.text)
linenum = linenum + 2
for option in self.options:
if (linenum - 2) > height - 5:
break
index = self.options.index(option)
if offset > index:
continue
label = " " + str(index + 1) + ". " + option.label + " "
if option.selected:
stdscr.addstr(linenum, 0, label, curses.color_pair(3))
else:
stdscr.attron(curses.color_pair(2))
stdscr.addstr(linenum, 0, label, curses.color_pair(2))
stdscr.attroff(curses.color_pair(2))
linenum = linenum + 1
stdscr.attroff(curses.color_pair(2))
stdscr.attroff(curses.A_BOLD)
# Refresh the screen
stdscr.refresh()
# Wait for next input
k = stdscr.getch()
Display More