It feels like years since I posted something technical; I have a busy day job and at least one side gig to thank for that. Thought I’d share the following python code, handy for wrapping a terse command line app (myApp) in a slightly more user friendly/cross platform gui. Don’t forget to re-introduce the persnickety python formatting 😉
from tkinter import *
from tkinter import ttk
from tkinter import filedialog
import subprocess
import time
import os
root = Tk()
# initialize parameters
programArgs = (' --a ', ' --b ', ' --c ')
argumentNames = ('Input File', 'Output Log', 'Output Directory', 'Output Prefix')
initMessage = 'Required'
argumentMap = { }
processID = ''
baseCommand='myApp.exe'
pathSep='\\'
# adapt syntax to OS
if os.name == 'posix':
baseCommand='myApp.x'
pathSep='/'
# test availability of binary
testCommand=''
try:
testCommand = subprocess.check_output(baseCommand, stdout=subprocess.PIPE)
if "allowed options" not in testCommand.communicate()[0]:
print('please install the myApp executable and make sure your PATH is correct')
time.sleep(3)
root.quit
except:
pass
# select a file or directory
def openFileDir(entry,field):
f=StringVar()
if 'Dir' not in field:
if 'Input' not in field:
f = filedialog.asksaveasfilename()
else:
f = filedialog.askopenfilename()
else:
f = filedialog.askdirectory()
if len(f) > 1:
argumentMap[field] = f
entry.delete(0,END)
entry.insert(0,f)
else:
argumentMap[field] = ''
entry.delete(0,END)
entry.insert(0,initMessage)
# get entry for prefix
def updateArgument(entry,field):
f=entry.get()
if "Required" not in f:
argumentMap[field]=f
else:
argumentMap[field]=''
# inform the user, uninitialized params
def printArgumentError(f):
msg="Please set a value for the parameter: "
msg+=f
print("%s" % (msg))
# create command
def createCommand():
v=StringVar()
comm=baseCommand + programArgs[0];
v=argumentMap[argumentNames[0]]
if len(v) <= 1:
printArgumentError(argumentNames[0])
return ''
comm+=v
comm+=programArgs[1];
v=argumentMap[argumentNames[1]]
if len(v) <= 1:
printArgumentError(argumentNames[1])
return ''
comm+=v
comm+=programArgs[2];
v=argumentMap[argumentNames[2]]
if len(v) <= 1:
printArgumentError(argumentNames[2])
return ''
comm+=v
comm+=pathSep
updateArgument(e4,argumentNames[3])
v=argumentMap[argumentNames[3]]
if len(v) <= 1:
printArgumentError(argumentNames[3])
return ''
comm+=v
print('%s' % comm)
time.sleep(10)
return comm
# run command
def runCommand():
comm=StringVar()
comm=createCommand()
if len(comm) <= 1:
msg="Can't execute myApp; missing required parameters"
print('%s' % msg)
return
subprocess.Popen(comm, stdout=subprocess.PIPE)
# create and grid the form
c = ttk.Frame(root)
c.grid(sticky="nsew")
c.master.title("myApp")
# create the widgets, first grid
# label, entry and browser for input
f1 = argumentNames[0];
l1 = ttk.Label(c, width=20, text=f1)
e1 = ttk.Entry(c,width=30)
e1.insert(0, initMessage)
b1 = ttk.Button(c, text='Browse',
command=lambda: openFileDir(e1,f1))
argumentMap[f1] = ' '
# label, entry and browser for output log
f2 = argumentNames[1]
l2 = ttk.Label(c, width=20, text=f2)
e2 = ttk.Entry(c,width=30)
e2.insert(0, initMessage)
b2 = ttk.Button(c, text='Browse',
command=lambda: openFileDir(e2,f2))
argumentMap[f2] = ' '
# label, entry and browser for output file
f3 = argumentNames[2]
l3 = ttk.Label(c, width=20, text=f3)
e3 = ttk.Entry(c,width=30)
e3.insert(0, initMessage)
b3 = ttk.Button(c, text='Browse',
command=lambda: openFileDir(e3,f3))
argumentMap[f3] = ' '
# label, entry for prefix
f4 = argumentNames[3]
l4 = ttk.Label(c, width=20, text=f4)
e4 = ttk.Entry(c,width=30)
e4.insert(0, initMessage)
argumentMap[f4] = ' '
# grid all the widgets
# first row, input
l1.grid(row=0, column=0, padx=5, pady=10)
e1.grid(row=0, column=1, pady=10)
b1.grid(row=0, column=2, padx=5)
# second row, output log
l2.grid(row=1, column=0, padx=5, pady=2)
e2.grid(row=1, column=1, pady=10)
b2.grid(row=1, column=2, padx=5)
# third row, output ldf
l3.grid(row=2, column=0, padx=5, pady=10)
e3.grid(row=2, column=1)
b3.grid(row=2, column=2, padx=5, pady=2)
# fourth row, output prefix
l4.grid(row=3, column=0, padx=5, pady=10)
e4.grid(row=3, column=1)
c.grid_columnconfigure(0, weight=1)
c.grid_columnconfigure(1, weight=1)
c.grid_columnconfigure(2, weight=1)
c.grid_rowconfigure(0, weight=1)
c.grid_rowconfigure(1, weight=1)
c.grid_rowconfigure(2, weight=1)
c.grid_rowconfigure(3, weight=1)
# second grid, action buttons
d = ttk.Frame(root)
d.grid(sticky="nsew")
b4 = Button(d, text='Quit', command=root.quit)
b5 = Button(d, text='Start', command=lambda: runCommand(),bg="green")
b6 = Button(d, text='Stop', command=root.quit,bg="red")
b4.grid(row=0, column=0, padx=5, pady=2)
b5.grid(row=0, column=1, padx=5, pady=2)
b6.grid(row=0, column=2, padx=5, pady=2)
# fix size
root.update()
root.minsize(root.winfo_width(), root.winfo_height())
root.maxsize(root.winfo_width(), root.winfo_height())
# run
root.mainloop()