Skip to content

Instantly share code, notes, and snippets.

@Aluriak
Created July 4, 2017 15:09
Show Gist options
  • Select an option

  • Save Aluriak/a5142039a87e45bc4d65935cb385c8ab to your computer and use it in GitHub Desktop.

Select an option

Save Aluriak/a5142039a87e45bc4d65935cb385c8ab to your computer and use it in GitHub Desktop.
clitogui poc
"""Proof of concept for Nedgang's clitogui project.
This code show how to extract very basic informations from
a argparse parser, with an example on the parser of another project.
At least the following features are not handled:
- nargs flag (default value is provided as is to argparse. See option --columns)
- action
- subparsers
- flags (see action_store in argparse doc)
"""
import argparse
from pprint import pprint
from collections import defaultdict
UNWANTED_DESTS = {'help', 'version'}
def reverse_dict(dct:dict) -> dict:
"""Associate each value with the keyS that link to it
>>> reverse_dict({1: 2, 2: 3, 3: 2})
{2: {1, 3}, 3: {2}}
"""
ret = defaultdict(set)
for k, v in dct.items():
ret[v].add(k)
return ret
def der_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--columns', '-c', nargs='+', default=('work', 'read', 'code'),
help='columns to show in each table')
parser.add_argument('--tables', '-t', help='number of table per page', default=3, type=int)
parser.add_argument('--lines', '-l', help='number of lines per table', default=56, type=int)
parser.add_argument('--padding', '-p', default=0.05, type=float,
help='ratio of the page dedicated to space between tables')
parser.add_argument('--output', '-o', help='name of the output tex file', type=str, # TODO: type=writable file
default='tables.tex')
return parser
def gen_widgets(parser:argparse.ArgumentParser=10):
# parser have both long and short options has key.
# We have to define the one to use (here, the longer).
dests = {option: action.dest for option, action in parser._option_string_actions.items()}
unique_dests = {dest: max(options, key=len) for dest, options in reverse_dict(dests).items() if dest not in UNWANTED_DESTS}
# Now, let's yield data related to each option
for option, action in parser._option_string_actions.items():
if option == unique_dests.get(action.dest):
yield option, action.type, action.dest, action.default, action.help
# pprint_action(action) # debug
def pprint_action(action):
type_widget = {int: 'Spinbox', None: 'Entry', str: 'Entry', float: 'Spinbox', bool: 'Checkbox'}
print('I have to create a {} to store the value for {} (default is {})'.format(
type_widget[action.type], action.dest, action.default
))
def clitogui(parser):
yield from gen_widgets(parser)
if __name__ == "__main__":
create_gui(der_parser())
"""Proof of concept for Nedgang's clitogui project.
This code build a GUI based on the information
given by clitogui generator.
"""
import os
import tkinter as tk
from tkinter import filedialog, simpledialog, font
from functools import partial
import clitogui
WINDOW_HEIGHT, WINDOW_WIDTH = 800, 600
class Interface(tk.Frame):
def __init__(self, parent, clitogui_actions, out_args:list, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.parent.geometry('{}x{}'.format(WINDOW_HEIGHT, WINDOW_WIDTH))
self.parent.title('Clitogui wrapper') # TODO: put encapsulated program name
self.actions = tuple(clitogui_actions)
self.out_args = out_args # will be populated with the cli argument designed by user
self.holders = {} # option: value holder of widgets
# Build the interface, populate self.holders
self.__create_widgets()
def __create_widgets(self):
for idx, (option, type, dest, default, help_msg) in enumerate(self.actions):
lab = tk.Label(self.parent, text=help_msg, width=40)
lab.grid(row=idx, column=0)
if type is int or type is float:
holder = tk.Variable(value=default)
wid = tk.Spinbox(self.parent, textvariable=holder)
elif type is str or type is None:
holder = tk.StringVar(value=default)
wid = tk.Label(self.parent, textvariable=holder)
elif type is bool:
holder = tk.Variable(value=value)
wid = tk.Checkbutton(self.parent, var=value_holder)
else:
raise TypeError('Unhandled type: {}.'.format(type))
wid.grid(row=idx, column=1)
self.holders[option] = holder
# Add run button
tk.Button(self.parent, text='Run !', command=self.apply).grid(row=idx+0, column=0, columnspan=2)
def apply(self):
"""Collect the parameters, populate self.out_args, then quit"""
# self.holders contains the value holder for each option,
# so we just have to add option, followed by holder's value
for option, holder in self.holders.items():
self.out_args.append(option)
self.out_args.append(str(holder.get()))
self.parent.quit() # kill the gui
if __name__ == "__main__":
root = tk.Tk()
# NB: args is populated by the gui. It's a way for the gui to
# communicate something to us.
parser, args = clitogui.der_parser(), []
Interface(root, clitogui.clitogui(parser), args)
root.mainloop() # run and wait the gui
# At this point, args have been populated, let's call the parser on it
print('ARGS:', args)
print('PARSER_CALL:', dir(parser.parse_args(args)))
# consequently, the only thing user have to provide is a argparse parser
# object. One funny way to encapsulate everything is to provide
# a decorator for end-user function that defines the parser.
# for instance:
def clitogui_decorator(func):
"""func is a function without param, and returning a parser"""
def wrapper():
# we just put the code above
root = tk.Tk()
parser, args = clitogui.der_parser(), []
Interface(root, clitogui.clitogui(parser), args)
root.mainloop() # run and wait the gui
# now the function returns the parser result, not the parser
return parser.parse_args(args)
return wrapper
# So, now, when you write a parser for your script, you can just
# wraps it with clitogui_decorator to get a gui.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment