ev3dev-lang icon indicating copy to clipboard operation
ev3dev-lang copied to clipboard

improve the button api

Open ensonic opened this issue 10 years ago • 17 comments

I am missing two feature from the button api.

  1. if I want to check if a key is pressed, where key is a variable I have to do:
getattr(ev3dev.buttons, key).pressed

This is not very discoverable.

2.) how would I check for any key pressed? If there would be an enum of available keys, I could check if any of them are pressed. We could also just have some convenience api for this.

ensonic avatar Oct 09 '15 14:10 ensonic

How about this?

ev3_keys = [
        'back'  : ev3dev.button.back,
        'left'  : ev3dev.button.left,
        'right' : ev3dev.button.right,
        'up'    : ev3dev.button.up,
        'down'  : ev3dev.button.down,
        'enter' : ev3dev.button.enter
        ]

def key_pressed(k):
    return ev3_keys[k].pressed()

def keys_pressed()
    return [k for k in ev3_keys if ev3_keys[k].pressed()]

ddemidov avatar Oct 09 '15 19:10 ddemidov

+1 for button.ev3_keys, or maybe just button.keys

button.key_pressed(k) is good.

maybe instead of button.keys_pressed -> button.any_key_pressed() (which would just check for keys_pressed() not returning an empty list)

ensonic avatar Oct 09 '15 19:10 ensonic

button.key_pressed(k) is good.

May be button.key[k].pressed() is even better (where key is a dict equal to ev3_keys above).

maybe instead of button.keys_pressed -> button.any_key_pressed() (which would just check for keys_pressed() not returning an empty list)

Would not you miss an opportunity to do something like

if 'left' in buttons.keys_pressed(): do_something_great()

or even

if set(['left', 'up']).issubset(buttons.keys_pressed()): do_something_even_greater()

?

ddemidov avatar Oct 09 '15 19:10 ddemidov

I don't think I'd miss them. For those I would probably rather write:

if buttons.left.isPressed(): go_left()

Given that there are just 6 keys, changes that two will be used for the same thing are probably quite low. We can also add both :)

ensonic avatar Oct 14 '15 15:10 ensonic

Well, you could do

if set(['left', 'up']).issubset(buttons.keys_pressed()): go_north_west()

:)

ddemidov avatar Oct 14 '15 15:10 ddemidov

Ok, I'll try to add this into python bindings.

ddemidov avatar Oct 14 '15 15:10 ddemidov

Should be fixed by ddemidov/ev3dev-lang-python@f535ed6c5201f4af035d0adc4e4b2fb3011c20db and ddemidov/ev3dev-lang-python@3e1a70a686771b4bd4a92220c91075f6b163e223.

ddemidov avatar Oct 14 '15 18:10 ddemidov

Thanks!

ensonic avatar Oct 15 '15 12:10 ensonic

@ensonic / @ddemidov - I am about to merge in the helper functions for the Button API, but @ddemidov's changes are specific to the EV3. I have 2 questions:

  1. Will the helpers still work as implemented [here][https://github.com/ddemidov/ev3dev-lang-python/commit/f535ed6c5201f4af035d0adc4e4b2fb3011c20db] without instantiating a Button now that we have a cache for the file descriptor?
  2. Should the any_pressed() function return None or an iterable list of pressed keys?

@ensonic - can you send along some sample code where you are using the Button API today, and where the helpers are used? The Button properties as lower case, like this:

if b.up:
    ....
if b.backspace:
    ....

rhempel avatar Oct 25 '15 15:10 rhempel

  1. Will the helpers still work as implemented [here][ddemidov/ev3dev-lang-python@f535ed6] without instantiating a Button now that we have a cache for the file descriptor?

Probably not. The properties (up, down, etc) used to be instances of Button class, so there was no need instantiating the main class (it was used simply as namespace). But it should be easy to reimplement the helper functions with getattr().

  1. Should the any_pressed() function return None or an iterable list of pressed keys?

I think @ensonic and I agreed that any_pressed() should return boolean value.

can you send along some sample code where you are using the Button API today?

I am not @ensonic, but this looks like what you are looking for: https://mp-devel.iais.fraunhofer.de/code/projects/ORA/repos/robertalab/browse/EV3MenuEv3dev/roberta/ev3.py#187

ddemidov avatar Oct 25 '15 15:10 ddemidov

If any_pressed() returns True what you typically end up doing is enumerating the buttons to find out which one is pressed :-) I'd still like a helper to return a list of pressed buttons - maybe list_pressed()?

Looking at @ensonic's code, the important bits for the Button API seem to be:

self.key = ev3dev.button
...
    # key
    def isKeyPressed(self, key):
        if key in ['any', '*']:
            # TODO: https://github.com/ev3dev/ev3dev-lang/issues/108
            for key in ['up', 'down', 'left', 'right', 'enter', 'back']:
                if getattr(self.key, key).pressed:
                  return True
            else:
                return False
        else:
            # remap some keys
            keys = {
              'escape':  'back',
              'backspace': 'back',
            }
            if key in keys:
                key = keys[key]
            # throws attribute error on wrong keys
            return getattr(self.key, key).pressed

    def isKeyPressedAndReleased(self, key):
        return False

And it's pretty clear that he's looking for an "any" operation with Boolean result. I'm going to add the helpers to the autogen template for the button properties because that way the platform specific list can be generated easily.

rhempel avatar Oct 25 '15 15:10 rhempel

If any_pressed() returns True what you typically end up doing is enumerating the buttons to find out which one is pressed :-) I'd still like a helper to return a list of pressed buttons - maybe list_pressed()?

I agree that could be helpful.

ddemidov avatar Oct 25 '15 15:10 ddemidov

Just confirming, I was indeed looking for something that returns true if any key is pressed. That is useful when e.g. showing an error.

ensonic avatar Oct 28 '15 01:10 ensonic

That's b.any - if you want the list of buttons that are pressed, b.buttons_pressed and if you want to check if a particular set of buttons is pressed, then b.check_buttons([...]) But I realize there's a bug - if there are more buttons pressed than specified. I'll fix that...basically we want all the buttons pressed to match all the buttons specified. and passing an empty list will return True if no buttons are pressed.

rhempel avatar Oct 28 '15 02:10 rhempel

I've read through the updates here but I'm unsure about how the current Button class works. What is the current notation for checking for a button press? I read the documentation on readthedocs but am not sure if that is updated. So if I want to check for whether the left button is pressed or not, should I write b.left, ev3.Button.left (if I have ev3dev imported as 'ev3'), or getattr(ev3dev.buttons, 'left').pressed? Thank you!

BookBytes avatar Aug 08 '16 15:08 BookBytes

Currently you have to create an instance of the Button class and use it to query button states:

btn = ev3.Button()
# Is 'Left' button pressed?
print('yes' if btn.left else 'no')

# Check if any buttons are pressed:
print('yes' if btn.any() else 'no')

# Do something when state of 'Left' button changes:
def report(state):
    print('pressed' if state else 'not pressed')
btn.on_left = report

while True:  # This loop checks buttons state continuously, calls appropriate event handlers
  btn.process()

ddemidov avatar Aug 09 '16 06:08 ddemidov

@ddemidov - Thank you. This is exactly what I was looking for. The button class makes much more sense now. Edit: And my code is now up and running correctly.

BookBytes avatar Aug 09 '16 15:08 BookBytes