alfred-workflow icon indicating copy to clipboard operation
alfred-workflow copied to clipboard

Tutorial options for keywords need to be updated for Alfred 4

Open elsatch opened this issue 5 years ago • 11 comments

As I was following the tutorials, I discovered that they Keywords screen has changed. Now there are two options for input: "with input as argv", "with input as {query}". The option "with input as argv" is selected by default.

Tutorial is written using "with input as {query}", so initially it doesn't match on the screen. There is no option to choose the Escaping options in the "with input as argv".

Alfred 4 Screenshot

elsatch avatar Aug 12 '19 18:08 elsatch

What exactly are you asking me to do?

There is no option to choose the Escaping options in the "with input as argv".

Escaping isn't necessary when using argv.

deanishe avatar Aug 12 '19 20:08 deanishe

In section "Adding a Script Filter" of the tutorial, under the "And enter the details for this action (the Escaping options don’t matter at the moment because our script currently doesn’t accept a query):" there is a screenshot.

In Alfred 4, the options for Escaping do not appear unless you manually select "with input a {query} in the dropdown menu".

Down in the same section, there is this sentence: "We’re going to create the pinboard.py script in a second. The Escaping options don’t matter for now because our Script Filter doesn’t accept an argument." I would add at the end of that phrase. (Note/Warning: If you are using Alfred 4, you might need to select "with input as {query}" in the dropdown menu to make the Escaping options appear. But don't worry now, as we will not be using them right now".

I was checking the repo contents, and I've seen the tutorial itself is at docs/tutorial_1.rst. If you prefer, I can create a pull request with suggested changes and you can review them.

elsatch avatar Aug 14 '19 00:08 elsatch

If you prefer, I can create a pull request with suggested changes and you can review them.

I wouldn't bother at this point. The library needs rewriting for Catalina/Python 3.

deanishe avatar Aug 14 '19 09:08 deanishe

I wouldn't bother at this point. The library needs rewriting for Catalina/Python 3.

Does that mean a rewrite for Python 3 is planned or not?

sniarn avatar Sep 12 '19 07:09 sniarn

Does that mean a rewrite for Python 3 is planned or not?

Well, yes. That's why I'm advising against big updates to the current version.

It'll be almost a complete rewrite for Python 3 only, and it's going to look a lot more like a Pythonic version of my Go library than it will the current version.

I want to remove a lot of stuff. Obviously, the Alfred 2- and 3-specific code is going, but there are still a few other things I need to decide on, such as whether to drop support for JSON in the caching code.

My main goal, apart from tidying up the API, is to improve loading time.

I'm open to any input regarding the API, features and docs (especially #103).

deanishe avatar Sep 12 '19 21:09 deanishe

Hmm. Perhaps something that would make it easier (or at least clearer) on how to write workflows that contain both Script Filter and Run Script actions. Today I always end up with a lot of deeply nested code. I'm not sure how this would work exactly, but the thought of it is nice.

sniarn avatar Sep 16 '19 09:09 sniarn

Perhaps something that would make it easier (or at least clearer) on how to write workflows that contain both Script Filter and Run Script actions.

I have an idea for the next tutorial that will have more parts and be a more complex workflow, combining multiple (free) APIs. That should give people a better idea how to deal with lots of actions.

Today I always end up with a lot of deeply nested code.

How so? Do you have an example?

deanishe avatar Sep 16 '19 14:09 deanishe

I have an idea for the next tutorial that will have more parts and be a more complex workflow, combining multiple (free) APIs. That should give people a better idea how to deal with lots of actions.

Perhaps a workflow that has two (or more) Script Filter actions that are connected sequentially. The first Script Filter would then trigger a secondary Script Filter displaying different options depending on which action was chosen in the first Script Filter. But then again, perhaps that would be a bit too much for a tutorial.

How so? Do you have an example?

Perhaps deeply nested is a bit of a stretch. I think my main point is that there are two very different ways of providing feedback to Alfred depending on whether the script is invoked by a Script Filter or a Run Script action. I typically end up with code like this:

if args.mode == 'something':
    # do something
    wf.send_feedback()
elif args.mode == 'something-else':
    # do something else
    v = Variables('blah')
    print(v)
elif args.mode == 'something-else-entirely':
    # do something else entirely
    wf.send_feedback()

sniarn avatar Sep 16 '19 18:09 sniarn

You can't really avoid all the ifelif clauses without a bit of "magic", but normally, once it starts getting complicated, you'd have each clause call a function instead of inlining the code:

if args.mode == 'something':
    return do_something()
elif args.mode == 'something-else':
    return do_something_else()
elif args.mode == 'something-else-entirely':
    return do_something_else_entirely()

If you have a large number of "modes", you can use reflection to call functions based on the value of mode:

def do_something():
    print('did something')

def do_something_else():
    print('did something else')

def do_something_else_entirely():
    print('did something else entirely')


def run(mode):
    name = 'do_' + mode.replace('-', '_')
    func = globals().get(name)
    if func:
        return func()
    raise ValueError('Unknown mode: ' + mode)


run(args.mode)

Or encapsulate your program in a class with the same technique:

class Program(object):
    """An example program."""

    def do_something(self):
        print('did something')

    def do_something_else(self):
        print('did something else')

    def do_something_else_entirely(self):
        print('did something else entirely')

    def run(self, mode):
        """Call method for ``mode``."""
        name = 'do_' + mode.replace('-', '_')
        meth = getattr(self, name)
        if meth:
            return meth()
        raise ValueError('Unknown mode: ' + mode)


p = Program()
p.run(args.mode)

deanishe avatar Sep 16 '19 19:09 deanishe

I think I used a dictionary at one point to do something similar. I'll try to clean up my code. Thanks for the tips!

Looking forward to see what you come up with for the Python 3 version of alfred-workflow.

sniarn avatar Sep 16 '19 19:09 sniarn

I think I used a dictionary at one point to do something similar

That works well, too. The advantage of using reflection is that you don't have to remember to add new functions to the dictionary.

My Zothero workflow is a pretty big one. That might be worth checking out as an example of how to structure a large workflow, even if the workflow itself isn't very interesting for you.

I tend to build my workflows as command-line programs, and it might be worth looking at other Python CLI programs, like beets.

deanishe avatar Sep 16 '19 19:09 deanishe