Git Product home page Git Product logo

clitogui's People

Contributors

aluriak avatar natir avatar nedgang avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

Forkers

aluriak xgid

clitogui's Issues

Circumventing GUI generation without messing with command line arguments

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.

Output in GUI

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,โ€ฆ).

Embellishment of the GUI

Current GUI is simple and efficient.

Some ideas:

  • showing program name and version, if such information are provided to the parser
  • showing program description
  • put tooltips on arguments, based on the help value

Support for docpie

Docpie aims to be like docopt, but maintained and with more features.

It may be impossible to reproduce all features in argparse.

Access to version

Argparse 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.

Support for float type

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'

Behavior in absence of arguments

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 ?

"Program embedding in clitogui", "repeat mode",

Problem

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:

  • i have to code that interface.
  • i got to run the clitogui GUI one time: no back and forth between input and output
  • consequently, if i pass one hour tuning hundreds of parameters and finally got an image i didn't like because of one parameter, i have to start over from the beginning

Proposal

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:

  • create the GUI, with (1) widgets allowing user to modify arguments, and (2) an area to print the outputs
  • whenever user hits the button "run", the callback is called with the arguments.
  • outputs view is updated accordingly
  • whenever user hits the button "done", the callback is called with the arguments, and the GUI exits, returning the last output.

Outputs view

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))

Other arguments

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.

    """

Support for docopt

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).

Handling of user defined types

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.