Created
July 4, 2017 15:09
-
-
Save Aluriak/a5142039a87e45bc4d65935cb385c8ab to your computer and use it in GitHub Desktop.
clitogui poc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """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()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """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