Repository: kivy/kivy

Issue:
exit_on_escape does not work as documented
i use 

Config.set('kivy', 'exit_on_escape', 0)

but still the application terminates on esc-keypress. i considere escape as a unnusal choice anyway as Ctrl-Q is the more commone app-quit-key. i would suggest to make the behavior completly configurable by setting a key that terminates the app (esc may be the default), setting it to None wont' term the app on keypress.



Discussion:
Can you post your code where it doesn't work? Because it works for me. You have to set it before you import anything kivy related.

i used 1.7.1 back then and when i switched to 1.8 i think it worked after a restart of the app. the modules do not react on config changes as they determine the value at import.  i assume that is the cause you have to restart the app.

as i don't like the whole thing and would rather see a configurable key to set an exit-key i implemented that (see my fork), which i personally like better. if you like it, take it. i wont do a PR as i assume you don't. ;)

i removed the osx code (ctrl-q) and would suggest to move that to the config module to have ctrl-q on osx and escape elsewhere as default. i think having a list of exit keys would be a overkill. 

I'm not the person that decides these things, but it should be easy (with one extra line) to check the config value of exit_on_escape when the program receives an exit input instead of import time. But in any case, you don't need to restart the program as long as you change the config value before you import anything else.

I didn't bother to look at @emdete's implementation, but here's how I would implement this:
1. add an _exit_key attribute to the base window class
2. create a new config value called 'exit_key'[1]
3. if exit_on_escape is True, set exit_key to escape
4. add a callback to the exit_key option if necessary to ensure that we don't need to reimport anything or restart the app for changes to take place

If everyone is keen, I might start this over the next weekend. I won't do it now because while the code changes to implement this are rather minimal, I'd have to ensure that the deprecation of exit_on_escape was documented, the new config value was documented, as well as the addition to `kivy.core.window`. 

@aspidites that shouldn't be needed. All that is needed is to move Config.getboolean('kivy', 'exit_on_escape') into the code to read every time an escape key is received. Now, it's imported once at the top, that's why it doesn't change unless the value is changed before window is imported.

@matham, doing so only addresses @emdete immediate concern of the key not working once the value is changed. My overall strategy was to address the  concern of the key not being configurable as well.

Also, by doing so, rather than first having to check if a boolean is set **then** having to check against a key value (as is done now), the individual window classes would simply have to check the keycode against the configured value. 

Finally, the reason for the callback is to only have to check what that key value once it changes, rather than at every iteration of the event loop.

Sorry, I had forgotten about the part where he wanted to change the exit key.


Resolving patch:
diff --git a/kivy/core/window/__init__.py b/kivy/core/window/__init__.py
--- a/kivy/core/window/__init__.py
+++ b/kivy/core/window/__init__.py
@@ -16,7 +16,7 @@
 from kivy.clock import Clock
 from kivy.config import Config
 from kivy.logger import Logger
-from kivy.base import EventLoop
+from kivy.base import EventLoop, stopTouchApp
 from kivy.modules import Modules
 from kivy.event import EventDispatcher
 from kivy.properties import ListProperty, ObjectProperty, AliasProperty, \
@@ -299,7 +299,7 @@ def _set_clearcolor(self, value):
     '''Color used to clear the window.
 
     ::
-    
+
         from kivy.core.window import Window
 
         # red background color
@@ -787,6 +787,22 @@ def on_keyboard(self, key, scancode=None, codepoint=None,
                            "codepoint instead, which has identical "
                            "semantics.")
 
+        # Quit if user presses ESC or the typical OSX shortcuts CMD+q or CMD+w
+        # TODO If just CMD+w is pressed, only the window should be closed.
+        is_osx = platform == 'darwin'
+        if self.on_keyboard.exit_on_escape:
+            if key == 27 or all([is_osx, key in [113, 119], modifier == 1024]):
+                stopTouchApp()
+                self.close()
+                return True
+    if Config:
+        on_keyboard.exit_on_escape = Config.get('kivy', 'exit_on_escape')
+
+        def __exit(section, name, value):
+            WindowBase.on_keyboard.exit_on_escape = value
+
+        Config.add_callback(__exit, 'kivy', 'exit_on_escape')
+
     def on_key_down(self, key, scancode=None, codepoint=None,
                     modifier=None, **kwargs):
         '''Event called when a key is down (same arguments as on_keyboard)'''
@@ -914,7 +930,7 @@ def request_keyboard(self, callback, target, input_type='text'):
         '''.. versionadded:: 1.0.4
 
         Internal widget method to request the keyboard. This method is rarely
-        required by the end-user as it is handled automatically by the 
+        required by the end-user as it is handled automatically by the
         :class:`~kivy.uix.textinput.TextInput`. We expose it in case you want
         to handle the keyboard manually for unique input scenarios.
 
@@ -947,7 +963,7 @@ def request_keyboard(self, callback, target, input_type='text'):
         :Return:
             An instance of :class:`Keyboard` containing the callback, target,
             and if the configuration allows it, a
-            :class:`~kivy.uix.vkeyboard.VKeyboard` instance attached as a 
+            :class:`~kivy.uix.vkeyboard.VKeyboard` instance attached as a
             *.widget* property.
 
         '''
diff --git a/kivy/core/window/window_pygame.py b/kivy/core/window/window_pygame.py
--- a/kivy/core/window/window_pygame.py
+++ b/kivy/core/window/window_pygame.py
@@ -20,11 +20,6 @@
 from kivy.utils import platform
 from kivy.resources import resource_find
 
-# When we are generating documentation, Config doesn't exist
-_exit_on_escape = True
-if Config:
-    _exit_on_escape = Config.getboolean('kivy', 'exit_on_escape')
-
 try:
     android = None
     if platform == 'android':
@@ -256,22 +251,6 @@ def screenshot(self, *largs, **kwargs):
         Logger.debug('Window: Screenshot saved at <%s>' % filename)
         return filename
 
-    def on_keyboard(self, key, scancode=None, codepoint=None,
-                    modifier=None, **kwargs):
-
-        codepoint = codepoint or kwargs.get('unicode')
-        # Quit if user presses ESC or the typical OSX shortcuts CMD+q or CMD+w
-        # TODO If just CMD+w is pressed, only the window should be closed.
-        is_osx = platform == 'darwin'
-        if _exit_on_escape and (key == 27 or
-                                (is_osx and key in (113, 119) and
-                                 modifier == 1024)):
-            stopTouchApp()
-            self.close()  # not sure what to do here
-            return True
-        super(WindowPygame, self).on_keyboard(
-            key, scancode, codepoint=codepoint, modifier=modifier)
-
     def flip(self):
         pygame.display.flip()
         super(WindowPygame, self).flip()
diff --git a/kivy/core/window/window_sdl.py b/kivy/core/window/window_sdl.py
--- a/kivy/core/window/window_sdl.py
+++ b/kivy/core/window/window_sdl.py
@@ -22,11 +22,6 @@
 from kivy.input.motionevent import MotionEvent
 from collections import deque
 
-# When we are generating documentation, Config doesn't exist
-_exit_on_escape = True
-if Config:
-    _exit_on_escape = Config.getboolean('kivy', 'exit_on_escape')
-
 
 class SDLMotionEvent(MotionEvent):
     def depack(self, args):
@@ -286,17 +281,6 @@ def mainloop(self):
     def _pygame_update_modifiers(self, mods=None):
         return
 
-    def on_keyboard(self, key, scancode=None, str=None, modifier=None):
-        # Quit if user presses ESC or the typical OSX shortcuts CMD+q or CMD+w
-        # TODO If just CMD+w is pressed, only the window should be closed.
-        is_osx = sys.platform == 'darwin'
-        if _exit_on_escape and (key == 27 or (is_osx and key in (113, 119) and
-                                              modifier == 1024)):
-            stopTouchApp()
-            self.close()  # not sure what to do here
-            return True
-        super(WindowSDL, self).on_keyboard(key, scancode, str, modifier)
-
     def request_keyboard(self, callback, target, input_type='text'):
         self._sdl_keyboard = super(WindowSDL, self).request_keyboard(
             callback, target, input_type)


Test patch: