ev3dev / ev3dev-lang

(deprecated) language bindings for ev3dev sensors, motors, LEDs, etc.
GNU General Public License v2.0
56 stars 39 forks source link

improve the button api #108

Open ensonic opened 9 years ago

ensonic commented 9 years ago

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.

ddemidov commented 9 years ago

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()]
ensonic commented 9 years ago

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

ddemidov commented 9 years ago

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

?

ensonic commented 9 years ago

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

ddemidov commented 9 years ago

Well, you could do

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

:)

ddemidov commented 9 years ago

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

ddemidov commented 9 years ago

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

ensonic commented 9 years ago

Thanks!

rhempel commented 9 years ago

@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:
    ....
ddemidov commented 9 years ago
  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

rhempel commented 9 years ago

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.

ddemidov commented 9 years ago

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.

ensonic commented 9 years ago

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.

rhempel commented 9 years ago

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.

BookBytes commented 8 years ago

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!

ddemidov commented 8 years ago

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()
BookBytes commented 8 years ago

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