How to disable user input or keyboard until animation completes?

561 views
Skip to first unread message

Max Fritzler

unread,
Feb 13, 2022, 5:37:08 PM2/13/22
to Kivy users support
I'm return to a kivy app that deals cards one by one.  A main function prepare() calls deal_cards().   deal_cards() calls an Animate object, with a callback to deal_cards.  That continues until the number of cards dealt reaches the desired number.

Control will return to the main function prepare() long before the animations complete.
You can't really prevent that.  But it would be wise to disable user input until the animations complete, and there are posts which seem to show how to do that.  If only I could figure them out.

Why doesn't this work?  When embedded in the larger application and run, I test it by entering text in a textbox, and observe via the debugger that disabled_keyboard() is indeed accessed, and that pressing the Escape key does indeed run self.new_game().
I was expecting all other keycodes to be ignored, but they are not, they are passed onto the text box and appear there.

Thanks in advance for any ideas.

def disabled_keyboard(self, keyboard, keycode, text, modifiers, *args):
# Do not process any keyboard events except Escape
print('made it to disabled_keyboard')
if keycode==27:
self.new_game()
Return

def disable_keyboard(self):
"""
Disable by binding it to a no-op.
"""
Window.bind(on_key_down=self.disabled_keyboard)


def enable_keyboard(self):
"""
Enable the keyboard by releasing the binding to disabled_keyboard
"""
Window.unbind(on_key_down=self.disabled_keyboard)


Robert

unread,
Feb 13, 2022, 7:09:27 PM2/13/22
to Kivy users support
The handler needs to return True if it consumes the event and False if the event is passed to Kivy.

I caution against testing with Esc and textinput, I think Kivy does some other filtering. Don't ask because I don't remember.

Elliot Garbus

unread,
Feb 13, 2022, 7:50:41 PM2/13/22
to kivy-...@googlegroups.com

You have a condition, that when true you want the keyboard input to be disabled.

Create a variable that holds the condition, lets call it keyboard_disabled.

When you start your animations set keyboard_disabled = True.

When your last animation is completed set keyboard disabled = False

 

In your normal keyboard handling routines, check is the keyboard is disabled.  If keyboard_disabled:  then return False, do not process the key press.

If not keyboard_disabled: do your normal key processing.

 

If you want to disable the TextInput, use the widget attribute “disabled”.  If this is the case create a Boolean Kivy property do the processing as described above, and bind the property in kv.

 

TextInput:

    disabled: root.keyboard_disabled

--
You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/13009e17-2bce-48cb-893b-1ae8a46303d1n%40googlegroups.com.

 

Max Fritzler

unread,
Feb 16, 2022, 5:16:11 PM2/16/22
to Kivy users support
Thanks.   I used "disabled: root.keyboard_disabled" on the text input and some buttons I wanted temporarily disabled, and that works fine.

In the examples of keyboard listener routines I've seen, the keyboard binding always occurs inside a class of some type of layout or widget, like
class Gameplay(BoxLayout):
          ...
        def _bind_keyboard(self):
         """
         Bind the keyboard.
         """
         # Listen for keyboard events.
         self._keyboard = Window.request_keyboard(self._unbind_keyboard, self)
         self._keyboard.bind(on_key_down=self._on_kbd_down)

                                        ...
Then the keyboard bindings only apply when the focus is on that layout.
I have not seen that done for the App class.  That is, for
        class OmissionApp(App):
                      ...
            I do not see any examples keyboard request or binding commands.
Even if you can declare a keyboard handler for the app class, I don't see anything in the docs saying that it would be inherited by the children of the app class.  That is, unless I am mistaken, there is no single place to put a keyboard "off switch" for the entire application.  Is that right?
Thanks in advance
Max

Robert

unread,
Feb 16, 2022, 6:20:24 PM2/16/22
to Kivy users support

Elliot Garbus

unread,
Feb 16, 2022, 7:46:27 PM2/16/22
to kivy-...@googlegroups.com

If the question is, “Is there a global attribute to disable all keyboard input?”, I believe you are correct there is no global switch to disable the keyboard input.   

 

I’ve never had a need for this kind of control.  In my own apps I have rarely had to bind the keyboard and only use it when looking for special keys (up arrow…).  I do often create filters for TextInput widgets.  What is the use case that has you looking to disable keyboard input globaly?

 

I’ll add that if you disable a layout all of it’s children will be disabled.

Max Fritzler

unread,
Feb 17, 2022, 11:22:42 AM2/17/22
to Kivy users support
Thanks, Robert and Elliot for the help.
I used Robert's example and implemented it in my code.  It does appear to be successful in disabling keyboard input.  However, it does not disable the buttons.  Fortunately, I can use the technique Elliot suggested to disable and re-enable the buttons.

My objective is to use this project to really learn a lot about Kivy, for the usual reasons.  (I've written a pretty significant desktop application that uses QT.  The app is beautiful, and works, but QT is a pain, especially for me, as my background is in Visual Basic for Applications, and never C or C++.  Tkinter just seems bizzare.  And I gave WxPython a good try but found it so fiddly that I gave up.  And it is also a wrapper for C or C++.)

Here's the use case.  I'm writing a card game app, where at the start 44 cards out of a two-deck pack are dealt.  I animate that, using suggestions from Elliot, and I usually set it to take about 1 second to deal a card, like a human player would.  Control will return to the calling object long before the animation is finished, so you want to disable all user interface input until then.  The reason for preferring a single "on / off" switch for the human interface is just error-proofing your code.  With the solution using the KV file, as one adds buttons and fields, one has to remember to add the disabled: root.keyboard_disabled = True bit to the widget.  Knowing me, I'll forget that.  And, not being a real programmer, I'm unsuccessful at producing a complete class and object design up front, so I'll be 'discovering' the need for more UI widgets as time goes by.

Here's the background. I'm writing a simulator for a card game called 'Liverpool rummy'.  First I want to get the game working with a single human player, playing against 2-6 "bots" which will just be some classes in the code that make, initially, some dumb or random choices about how to play.  Then I'll go to simulation mode, with no human player, and the game storing configuration and moves in Sqlite3.  The player bots will each use a different strategy.  Run a few thousand games and see if one strategy is better than another.  By the time I get that working, I'll know Kivy pretty well, and will learn a lot about python, various toolkits, and how to design an application.

I need to get the user interface working with human-friendly animation speeds, so that when I switch to simulation mode and start recording and playing back games, I can watch the human interface to assist in debugging.  A real programmer would probably use PyTest and good class and object design to validate with automated PyTests as development goes along.  I'm an amateur, and while I can do some things with PyTest, I'll really want to watch the playback to see that it is really working correctly.

Anyway, I've got solutions to the animation timing problem for now that will work.  The next task is figuring out how to move the cards around in a player's hand and the piles or melds the player makes on the game board.  I'll check out the kivy.org tutorials again, of course.

Elliot Garbus

unread,
Feb 17, 2022, 11:31:56 PM2/17/22
to kivy-...@googlegroups.com

More to facilitate your leaning that to address any problem:

Here is a way to approach creating a global disable.  You could use the Widget method walk() to walk over the widget tree.  If you wanted, for example to disable all of the Buttons and TextInputs, you could use the python builtin isinstance() to test if a widget is a Button or TextInput and if it is sent the disabled attribute for the widget to True (or False).

 

To build your multiple selection capability you may want to consider using the Compound Selection Behavior.  https://kivy.org/doc/master/api-kivy.uix.behaviors.compoundselection.html

Reply all
Reply to author
Forward
0 new messages