nedgang / clitogui Goto Github PK
View Code? Open in Web Editor NEWAutomatic GUI generation from a CLI
Automatic GUI generation from a CLI
Currently, the only way to avoid clitogui to generate the gui is to add a --cli
option to sys.argv. This is ambiguous (it could be an argument used by the user) and not really malleable (you have to modify this before the call to clitogui).
Another way to go would be to inspect arguments passed to .parse_args()
function. If no arguments are given (None
), then the GUI should be run. However, if a list of command line arguments are provided, this means the arguments already have been devised. No GUI is needed.
Such a change would enable easy unit testing, without having to bother with sys.argv black magic.
In some cases, the output of the decorated python script is itself a displayable object (an image, typically).
Could we think about a way to get clitogui to not only decorate the CLI, but also to retrieve and print the final results ?
My use case is a generator of image generator : the program is generating a CLI, which generates an image depending of the CLI options. Currently, decorating the generated CLI with clitogui will only provide a GUI for the CLI, but the final output will have to be opened with another program.
What about something like:
def my_running_method(gui_options):
... # build the image, depending of GUI options
return image_path or image_bytes or image_pillow_object
@clitogui.shower(on_run=my_running_method)
def my_cli():
... # regular creation and return of argparse parser
my_cli(sys.argv).run()
The my_running_method
is basically called each time user presses run
button in the GUI generated by clitogui. The returned value would be handled by clitogui, depending of its nature (text, image,โฆ).
click is a very pythonic CLI system. Could be really easy to adapt the basic features.
Current GUI is simple and efficient.
Some ideas:
help
valueArgparse allows user to specify a version
command, allowing to print the current version and exit.
Example:
parser.add_argument('--version', '-V', action='version', version="%(prog)s " + __version__)
Such an information should be displayed in the GUI.
Current float handling is buggy:
import argparse
from clitogui import clitogui
@clitogui.clitogui
def cli():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("factor", type=float, default=0.1)
return parser
print(cli().parse_args())
Leading to the following stack trace:
Traceback (most recent call last):
File "examples/simple-parser.py", line 14, in <module>
print(cli().parse_args())
File "clitogui/clitogui.py", line 34, in gui_builder
gui = Interface(ExtractedParser(self))
File "clitogui/gui.py", line 46, in __init__
self.__create_widgets__(self.widget_layout, self.parser.arguments)
File "clitogui/gui.py", line 116, in __create_widgets__
widget = QLineEdit(action['default'])
TypeError: arguments did not match any overloaded call:
QLineEdit(parent: QWidget = None): argument 1 has unexpected type 'int'
QLineEdit(str, parent: QWidget = None): argument 1 has unexpected type 'int'
Sample code:
def cli():
parser = argparse.ArgumentParser(description=__doc__)
return parser
if __name__ == "__main__":
args = cli().parse_args()
print(args)
Current Interface is empty, just waiting for the user to push ok. Does that makes sens ?
Let's consider the following example, where function run_pipeline
is basically turning command line arguments into an image.
My goal is to let me create this image easily, using a GUI.
import biseau
from clitogui import clitogui
def run_pipeline(args):
... # some computations
return image # a pillow image
@clitogui
def cli():
parser = argparse.ArgumentParser(description=__doc__)
... # some arguments
return parser
if __name__ == "__main__":
args = cli().parse_args()
image = run_on(args)
run_gui_to_show(image)
Obviously, i'm doing my own interface to show the generated image.
There is few caveats with this approach:
The following proposal add a new function in the clitogui API, that reverse the embedding order of clitogui and user program, hence allowing to fix the above problems.
import biseau
from clitogui import clitogui
def run_pipeline(args):
... # some computations
return image # a pillow image
@clitogui.show_outputs_of(run_pipeline)
def cli():
parser = argparse.ArgumentParser(description=__doc__)
... # some arguments
return parser
if __name__ == "__main__":
args, final_image = cli().parse_args()
... # saving of the final image, and the args leading to it
The important bit here is the object clitogui.show_outputs_of
. When asked to parse_args()
, it implements the following behavior:
Obviously, this part is the more complex: it has to infer from the callback's output type how to show them to user.
Here is a first draft:
def widget_from_output_type(outputs) -> QWidget:
if isinstance(outputs, PIL.Image):
... # return an image viewer widget
elif isinstance(outputs, str):
... # return an (multiline) text label widget
... # etc
elif isinstance(outputs, tuple) and len(outputs) == 2:
... # specific case of the next
elif isinstance(outputs, tuple):
# multiple value: let's make a tabulated widget, each tab containing the widget of the output.
# this allows callback to return a complex mosaic of values (a text and many images for instance).
tabs = QTabWidget()
for output in outputs:
widget = widget_from_output_type(output)
tabs.addWidget(widget)
else:
raise NotImplementedError(f"output of type {type(outputs)} is not handled.")
The update outputs view part of the main function will therefore looks like:
while True:
args = argparse_parser.parse_args()
outputs = callback(args)
self.outputs_view.setWidget(widget_from_output_type(outputs))
The show_outputs_of
function could provide some options, and the following signature:
def show_outputs_of(on_resolve:callable, *, autorun:bool=False, cascade_outputs:bool=False, output_type_hook:[callable]):
"""Decorator of argparse.ArgumentParser, generating a GUI allowing user
to input arguments in a graphical manner, and view the results accordingly.
on_resolve -- callback which returns things to show to user, receiving the arguments from argparse and called whenever the user ask for it.
autorun -- also call the callback and update output view when an argument value is changed.
cascade_outputs -- instead of separating different outputs in a tab, organize them in a (vertical) cascade.
output_type_hook -- iterable of function taking output as argument, and returning a Widget to use as output view. To implement support for a non supported type.
return -- the value of the last call to on_resolve.
"""
Currently working on it, by converting docopt internal objects into argparse standard parser.
It is a whole project in itself, it seems.
I seriously consider making a package for that (especially since clitogui is not the only project that could benefit from that).
Current decoration is not sufficient. Use functools.wraps
instead.
Argparse enables user to implements their own type. Supporting such functions would be awesome.
To ensure the understanding of the undelying type clitogui will have to provide to the function, it is possible to use type annotation. For instance, here is an argparse type "existing file":
import os
import argparse
import clitogui
@clitogui.clitogui
def cli_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description='CLI for messing with clitogui.')
parser.add_argument('infile', type=existant_file, help='file containing the graph data')
return parser
def existant_file(filepath:str) -> str:
"""Argparse type, raising an error if given file does not exists"""
if not os.path.exists(filepath):
raise argparse.ArgumentTypeError("file {} doesn't exists".format(filepath))
return filepath
if __name__ == "__main__":
cli_parser().parse_args()
I'm working on it.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.