python3izing scripts

This commit is contained in:
Ville Rantanen
2020-10-12 11:28:07 +03:00
parent db3e281df6
commit 877acc0d35
4 changed files with 422 additions and 337 deletions

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
import math
import os
@@ -10,57 +10,125 @@ import time
import tty
readline.parse_and_bind('tab: complete')
readline.parse_and_bind('set editing-mode vi')
readline.parse_and_bind("tab: complete")
readline.parse_and_bind("set editing-mode vi")
MENUFILE = '.foldermenu'
DEFAULTFILE = os.path.expanduser(os.path.join('~','.config','foldermenu','default'))
MENUFILE = ".foldermenu"
DEFAULTFILE = os.path.expanduser(os.path.join("~", ".config", "foldermenu", "default"))
VERSION = "0.5"
DEBUG = False
def setup_options():
''' Setup the command line options '''
""" Setup the command line options """
from argparse import ArgumentParser
parser=ArgumentParser(description="Prints folder specific shell commands stored in '" + MENUFILE +
"' file, and in addition the executables in the current folder. " +
"Menufile format for each line: 'description:command'. " +
"If the command ends in '&' it is run in the background. " +
"If the command ends in '/' it is a folder, and selecting will enter. " +
"Extra keyboard shortcuts: / Edit, $ Shell ")
parser.add_argument("-1","--one-shot",action = 'store_true', dest = 'once', default = False,
help = "Launch only once, then exit")
parser.add_argument("-b","--no-banner", action='store_false', dest='banner', default=True,
help="Do not show banners. Banners are ## starting lines in the file")
parser.add_argument("-d","--no-defaults",action='store_false', dest='defaults',default=True,
help="Do not show default entries from "+DEFAULTFILE)
parser.add_argument("-x","--no-exec",action='store_false', dest='executables',default=True,
help="Do not show executables in the listing.")
parser.add_argument("--columns",'-f','-C',type=int,action='store', dest='columns',default=0,
help="Number of columns. 0 for automatic")
parser.add_argument("-l","--list",action='store_true', dest='list',default=False,
help="Print the list, don't wait for keypress.")
parser.add_argument("--command",'-c',type=str,action='store', dest='command',
help="Command to run (1-9a-z..), any argumets after -- are forwarded to the command ")
parser.add_argument("--no-colors",'--nc',action="store_false",dest="colors",default=True,
help="Disable colored output")
parser.add_argument("--horizontal",'-H',action="store_true",dest="horizontal",default=False,
help="Horizontal order of items, only valid for -l listing.")
parser.add_argument("--version",action='version', version=VERSION)
parser.add_argument("args",type=str,action="store",default="",nargs="?",
help="Arguments for the command, if -c used. The string will be re-parsed with shutils. Use '--' to skip local parsing.")
parser = ArgumentParser(
description="Prints folder specific shell commands stored in '"
+ MENUFILE
+ "' file, and in addition the executables in the current folder. "
+ "Menufile format for each line: 'description:command'. "
+ "If the command ends in '&' it is run in the background. "
+ "If the command ends in '/' it is a folder, and selecting will enter. "
+ "Extra keyboard shortcuts: / Edit, $ Shell "
)
parser.add_argument(
"-1",
"--one-shot",
action="store_true",
dest="once",
default=False,
help="Launch only once, then exit",
)
parser.add_argument(
"-b",
"--no-banner",
action="store_false",
dest="banner",
default=True,
help="Do not show banners. Banners are ## starting lines in the file",
)
parser.add_argument(
"-d",
"--no-defaults",
action="store_false",
dest="defaults",
default=True,
help="Do not show default entries from " + DEFAULTFILE,
)
parser.add_argument(
"-x",
"--no-exec",
action="store_false",
dest="executables",
default=True,
help="Do not show executables in the listing.",
)
parser.add_argument(
"--columns",
"-f",
"-C",
type=int,
action="store",
dest="columns",
default=0,
help="Number of columns. 0 for automatic",
)
parser.add_argument(
"-l",
"--list",
action="store_true",
dest="list",
default=False,
help="Print the list, don't wait for keypress.",
)
parser.add_argument(
"--command",
"-c",
type=str,
action="store",
dest="command",
help="Command to run (1-9a-z..), any argumets after -- are forwarded to the command ",
)
parser.add_argument(
"--no-colors",
"--nc",
action="store_false",
dest="colors",
default=True,
help="Disable colored output",
)
parser.add_argument(
"--horizontal",
"-H",
action="store_true",
dest="horizontal",
default=False,
help="Horizontal order of items, only valid for -l listing.",
)
parser.add_argument("--version", action="version", version=VERSION)
parser.add_argument(
"args",
type=str,
action="store",
default="",
nargs="?",
help="Arguments for the command, if -c used. The string will be re-parsed with shutils. Use '--' to skip local parsing.",
)
options = parser.parse_args()
if not os.path.exists(DEFAULTFILE):
options.defaults = False
return options
def termsize():
rows, columns = os.popen('stty size', 'r').read().split()
rows, columns = os.popen("stty size", "r").read().split()
return (int(rows), int(columns))
def ichr(i):
''' convert integer to 1-9, a-z, A-Z, omitting q,x '''
""" convert integer to 1-9, a-z, A-Z, omitting q,x """
if i < 10:
return str(i)
@@ -73,36 +141,37 @@ def ichr(i):
i += 64 - 122
return chr(i)
class bc:
MAG = '\033[35m'
BLU = '\033[34m'
GRE = '\033[32m'
YEL = '\033[33m'
RED = '\033[31m'
CYA = '\033[36m'
WHI = '\033[1m'
BG_BLK = '\033[40m'
END = '\033[0m'
CLR = '\033[2J'
INV = '\033[7m'
MAG = "\033[35m"
BLU = "\033[34m"
GRE = "\033[32m"
YEL = "\033[33m"
RED = "\033[31m"
CYA = "\033[36m"
WHI = "\033[1m"
BG_BLK = "\033[40m"
END = "\033[0m"
CLR = "\033[2J"
INV = "\033[7m"
def disable(self):
self.MAG = ''
self.BLU = ''
self.GRE = ''
self.YEL = ''
self.RED = ''
self.CYA = ''
self.WHI = ''
self.END = ''
self.BG_BLK = ''
self.INV = ''
self.MAG = ""
self.BLU = ""
self.GRE = ""
self.YEL = ""
self.RED = ""
self.CYA = ""
self.WHI = ""
self.END = ""
self.BG_BLK = ""
self.INV = ""
def pos(self,y,x):
return "\033[%s;%sH"%( y, x )
def pos(self, y, x):
return "\033[%s;%sH" % (y, x)
def posprint(self, y, x, s):
sys.stdout.write( self.pos(y, x) + str(s) )
sys.stdout.write(self.pos(y, x) + str(s))
class getch:
@@ -122,18 +191,21 @@ class getch:
class launch_item:
''' Class for launchable items '''
""" Class for launchable items """
def __init__(self, command, description, launcher):
self.command = command
self.description = description
self.launcher = launcher
class entry_collection:
''' Object containing the list items, and the printing methods '''
""" Object containing the list items, and the printing methods """
def __init__(self, options):
self.options = options
self.menu_keys=[ichr(i+1) for i in range(60)]
self.args = ''
self.menu_keys = [ichr(i + 1) for i in range(60)]
self.args = ""
self.co = bc()
self.dir_mode = False
self.max_length = 0
@@ -160,76 +232,58 @@ class entry_collection:
def set_args(self, args):
self.args = args
def read_menu(self, menu_file = MENUFILE):
''' Read the menu file '''
def read_menu(self, menu_file=MENUFILE):
""" Read the menu file """
if os.path.exists(menu_file):
with open(menu_file, 'rt') as f:
with open(menu_file, "rt") as f:
for row in f:
if row.strip() == '':
if row.strip() == "":
continue
if row[0:2] == '##':
self.banner.append(
row[2:].replace("\n","").replace("\r","")
)
if row[0:2] == "##":
self.banner.append(row[2:].replace("\n", "").replace("\r", ""))
continue
if row[0] == '#':
if row[0] == "#":
continue
row = row.strip().split(':',1)
row = row.strip().split(":", 1)
if len(row) == 1:
row = [ row[0].strip(), row[0] ]
row = [row[0].strip(), row[0]]
else:
row = [ row[0].strip(), row[1] ]
row = [row[0].strip(), row[1]]
if len(row[1]) == 0:
row[1] = " "
launcher = "menu"
if row[1][-1] == '/' and os.path.isdir(row[1]):
if row[1][-1] == "/" and os.path.isdir(row[1]):
launcher = "dir"
if row[0][-1] != "/":
row[0] += "/"
self.entries.append(
launch_item(
command = row[1],
description = row[0],
launcher = launcher
command=row[1], description=row[0], launcher=launcher
)
)
def read_folder(self):
''' Read folder contents, return executable files and dirs '''
""" Read folder contents, return executable files and dirs """
self.dirs.append(
launch_item(
command = '..',
description = '..',
launcher = "dir"
)
)
self.dirs.append(launch_item(command="..", description="..", launcher="dir"))
dirs = []
executables = []
for f in os.listdir('.'):
for f in os.listdir("."):
if os.path.isfile(f):
if os.access(f, os.X_OK):
executables.append(f)
if os.path.isdir(f) and f[0] != '.':
if os.path.isdir(f) and f[0] != ".":
dirs.append(f)
dirs.sort()
for d in dirs:
self.dirs.append(
launch_item(
command = d,
description = d + "/",
launcher = "dir"
)
launch_item(command=d, description=d + "/", launcher="dir")
)
if self.options.executables:
executables.sort()
for e in executables:
self.entries.append(
launch_item(
command = e,
description = e,
launcher = "exec"
)
launch_item(command=e, description=e, launcher="exec")
)
def entry_color(self, launcher):
@@ -246,11 +300,11 @@ class entry_collection:
helptext = "[.]commands"
my_entries = self.dirs
else:
helptext = '[.]folders [-]args %s(%s%s%s)'%(
helptext = "[.]folders [-]args %s(%s%s%s)" % (
self.co.END + self.co.MAG,
self.co.END,
self.args,
self.co.MAG
self.co.MAG,
)
my_entries = self.entries
@@ -271,34 +325,21 @@ class entry_collection:
else:
banner = []
blen = len(banner)
cwd = os.path.basename( os.getcwd() )[0:15]
cwd = os.path.basename(os.getcwd())[0:15]
self.co.posprint(
1,
1,
self.co.END + self.co.CLR
)
self.co.posprint(1, 1, self.co.END + self.co.CLR)
self.co.posprint(
1,
3,
"%s%s%s [q/x]exit %s%s"%(
self.co.WHI,
cwd,
self.co.MAG,
helptext,
self.co.END
)
"%s%s%s [q/x]exit %s%s"
% (self.co.WHI, cwd, self.co.MAG, helptext, self.co.END),
)
for i, e in enumerate(banner):
self.co.posprint(
i+2,
0,
e
)
rows = int( math.ceil( len(my_entries) / pars ) )
self.co.posprint(i + 2, 0, e)
rows = int(math.ceil(len(my_entries) / pars))
while rows > maxrows:
pars += 1
rows = int( math.ceil( len(my_entries) / pars ) )
rows = int(math.ceil(len(my_entries) / pars))
maxcolumns = int(math.ceil(maxcolumns / pars))
r = 1 + blen
par = 1
@@ -313,16 +354,17 @@ class entry_collection:
column = 2
border = ""
else:
column = maxcolumns * ( par - 1 )
border = '| '
column = maxcolumns * (par - 1)
border = "| "
if self.selected == index:
highlight = self.co.INV + ">"
else:
highlight = ' '
highlight = " "
self.co.posprint(
r+1,
r + 1,
column,
"%s%s%s%s%s%s%s%s"%(
"%s%s%s%s%s%s%s%s"
% (
border,
self.co.WHI,
key,
@@ -330,16 +372,13 @@ class entry_collection:
self.entry_color(entry.launcher),
highlight,
printline,
self.co.END
)
self.co.END,
),
)
r += 1
self.co.posprint(
rows + 2 + blen,
0,
"#"
)
self.co.posprint(rows + 2 + blen, 0, "#")
self.rows = rows
sys.stdout.flush()
def list(self):
""" draws the list at cursor """
@@ -350,18 +389,13 @@ class entry_collection:
if self.options.columns == 0:
pars = 1.0
if len(self.entries) > 9:
pars = max(
1.0,
math.floor(
maxcolumns / float(self.max_length)
)
)
pars = max(1.0, math.floor(maxcolumns / float(self.max_length)))
while len(self.entries) / pars < pars:
pars -= 1
else:
pars = float(self.options.columns)
rows = int(math.ceil( len(self.entries) / float(pars) ))
rows = int(math.ceil(len(self.entries) / float(pars)))
maxcolumns = int(math.floor(maxcolumns / pars)) - 2
# If names won't fit the columns, make sure at least 3 characters are visible
if maxcolumns < 6:
@@ -370,8 +404,8 @@ class entry_collection:
origmaxcolumns -= 10
while maxcolumns < 6:
pars -= 1
rows = int(math.ceil( len(self.entries) / float(pars) ))
maxcolumns = int(math.floor( origmaxcolumns / pars )) - 2
rows = int(math.ceil(len(self.entries) / float(pars)))
maxcolumns = int(math.floor(origmaxcolumns / pars)) - 2
self.max_length = min(maxcolumns, self.max_length)
if self.options.horizontal:
@@ -380,24 +414,24 @@ class entry_collection:
for r in range(int(rows)):
formatted.append([])
for p in range(int(pars)):
formatted[r].append(' '*(self.max_length))
formatted[r].append(" " * (self.max_length))
if self.options.horizontal:
formatted[r][p] += ' '
formatted[r][p] += " "
r = 0
par = 0
for entry,key in zip(self.entries, self.menu_keys):
for entry, key in zip(self.entries, self.menu_keys):
if r >= rows:
par += 1
r = 0
printline = entry.description[:(maxcolumns-3)]
printline += ' '*(self.max_length-len(printline)-1)
formatted[r][par]="%s%s%s %s%s%s"%(
printline = entry.description[: (maxcolumns - 3)]
printline += " " * (self.max_length - len(printline) - 1)
formatted[r][par] = "%s%s%s %s%s%s" % (
self.co.WHI,
key,
self.co.END,
self.entry_color(entry.launcher),
printline,
self.co.END
self.co.END,
)
r += 1
if self.options.horizontal:
@@ -407,10 +441,10 @@ class entry_collection:
if self.banner:
print("\n".join(self.banner))
for row in formatted:
print('|'.join(row))
print("|".join(row))
def launch(self, key):
''' launch the given entry '''
""" launch the given entry """
bg = False
idx = self.menu_keys.index(key)
@@ -428,14 +462,14 @@ class entry_collection:
self.selected = -1
return
if command_str[-1] == '&':
#Run the program in background
if command_str[-1] == "&":
# Run the program in background
command_str = command_str[:-1]
bg = True
if len(self.args) > 0:
command_str += ' ' + self.args
if self.entries[idx].launcher == 'exec':
command_str = './' + command_str
command_str += " " + self.args
if self.entries[idx].launcher == "exec":
command_str = "./" + command_str
if not self.options.command:
print(command_str)
@@ -443,24 +477,24 @@ class entry_collection:
if bg:
subprocess.Popen(
command_str,
stderr = subprocess.PIPE,
stderr=subprocess.PIPE,
shell=True,
executable="/bin/bash"
executable="/bin/bash",
)
else:
subprocess.call(
command_str,
stderr = subprocess.STDOUT,
shell = True,
executable = "/bin/bash"
stderr=subprocess.STDOUT,
shell=True,
executable="/bin/bash",
)
except:
print('Non-zero exit code: "' + command_str + '"')
if not (self.options.command or self.options.once or bg):
print('Press any key...')
ch=getch()
inkey=ord(ch.get())
print("Press any key...")
ch = getch()
inkey = ord(ch.get())
def flip_mode(self):
self.dir_mode = not self.dir_mode
@@ -474,10 +508,10 @@ class entry_collection:
try:
idx = self.menu_keys.index(key)
except ValueError:
return (False, 'Not a possible key')
return (False, "Not a possible key")
if idx + 1 > my_len:
return (False, 'No such entry')
return (True, '')
return (False, "No such entry")
return (True, "")
def select_move(self, delta):
new_value = self.selected + delta
@@ -493,30 +527,27 @@ class entry_collection:
def edit_menu(self):
subprocess.call(
"vim %s"%( MENUFILE, ),
stderr = subprocess.STDOUT,
shell = True,
executable = "/bin/bash"
"vim %s" % (MENUFILE,),
stderr=subprocess.STDOUT,
shell=True,
executable="/bin/bash",
)
self.initialize()
def shell(self):
#env = os.environ.copy()
#env["PS1"] = "fM:" + env.get("PS1","")
# env = os.environ.copy()
# env["PS1"] = "fM:" + env.get("PS1","")
subprocess.call(
"bash --rcfile <(cat ~/.bashrc; echo \"PS1='folderMenu: '\$PS1\") -i",
stderr = subprocess.STDOUT,
shell = True,
executable = "/bin/bash"
stderr=subprocess.STDOUT,
shell=True,
executable="/bin/bash",
)
self.initialize()
def debug_code_print(c):
print("- code: %d, str: %s -"%(
c,
str(c)
))
print("- code: %d, str: %s -" % (c, str(c)))
time.sleep(1)
@@ -565,25 +596,25 @@ def start_engines():
# 53 = pg up
# 54 = pg down
#
#~ print(inkey3)
#~ sys.exit(0)
# ~ print(inkey3)
# ~ sys.exit(0)
if inkey in (113, 120, 3, 24, 4): # q, x
print('Exited in: ' + os.getcwd())
if inkey in (113, 120, 3, 24, 4): # q, x
print("Exited in: " + os.getcwd())
sys.exit(0)
if inkey == 45: # -
print('')
if inkey == 45: # -
print("")
readline.set_startup_hook(lambda: readline.insert_text(entries.args))
args = raw_input('args: ')
args = raw_input("args: ")
entries.set_args(args)
readline.set_startup_hook(None)
if inkey == 46: # .
if inkey == 46: # .
entries.flip_mode()
if inkey == 47: # /
if inkey == 47: # /
entries.edit_menu()
if inkey == 36: # $
if inkey == 36: # $
entries.shell()
if inkey == 13: # enter
if inkey == 13: # enter
inkey = ord(entries.menu_keys[entries.selected])
found, message = entries.is_key(chr(inkey))
if found:
@@ -592,7 +623,6 @@ def start_engines():
sys.exit(0)
entries.initialize()
if __name__ == "__main__":
start_engines()