Question

I've been using enaml (0.6.8 which is what is available with Canopy for now) and have successfully created some very useful utility applications. I would like, however, to intercept keyboard events to enable some quick keyboard shortcuts rather than repetitive button-clicking in the UI.

How do I approach this? I understand that this will be toolkit (qt4) specific, but cannot really figure out where to begin. I've read some on event filters in qt, which seem like what I might want, but I do not understand the mechanisms for relating QApplication, etc. to enaml

Was it helpful?

Solution

Intercepting raw key events is not currently supported, unless you want to work directly with the toolkit widget via foo.proxy.widget (on the 0.7+ series; the 0.6 series is no longer supported). You can, however, define a MenuBar with Actions which support accelerator keys triggered by custom action names such as "Cut\tCtrl+C" and "Paste\tCtrl+V".

OTHER TIPS

I created a rather crude way of doing this. Make a file named key_event.py with the following:

# -*- coding: utf-8 -*-
'''
Created on Jul 20, 2015
@author: jrm
'''
from atom.api import (Callable,Event, Value, Unicode, Bool, Instance,Typed, ForwardTyped, observe)
from enaml.core.declarative import d_
from enaml.widgets.control import Control, ProxyControl
from enaml.qt.qt_control import QtControl
from enaml.qt import QtCore

class ProxyKeyEvent(ProxyControl):
    declaration = ForwardTyped(lambda: KeyEvent)

    def set_enabled(self, enabled):
        raise NotImplementedError

class KeyEvent(Control):
    proxy = Typed(ProxyKeyEvent)

    key_code = d_(Value())
    key = d_(Unicode())
    enabled = d_(Bool(True))
    repeats = d_(Bool(True))

    pressed = d_(Event(),writable=False)
    released = d_(Event(),writable=False)

    @observe('enabled')
    def _update_proxy(self, change):
        """ An observer which sends state change to the proxy.
        """
        super(KeyEvent, self)._update_proxy(change)

class QtKeyEvent(QtControl, ProxyKeyEvent):
    _keyPressEvent = Callable() # Refs to original functions
    _keyReleaseEvent = Callable() # Refs to original functions
    widget = Instance(QtCore.QObject)

    def create_widget(self):
        self.widget = self.parent_widget()
    def init_widget(self):
        super(QtKeyEvent, self).init_widget()
        d = self.declaration
        widget = self.parent_widget()
        self._keyPressEvent = widget.keyPressEvent
        self._keyReleaseEvent = widget.keyPressEvent
        self.set_enabled(d.enabled)

    def set_enabled(self, enabled):
        widget = self.parent_widget()
        if enabled:
            widget.keyPressEvent = lambda event:self.on_key_press(event)
            widget.keyReleaseEvent = lambda event:self.on_key_release(event)
        else:
            # Restore original
            widget.keyPressEvent = self._keyPressEvent
            widget.keyReleaseEvent = self._keyReleaseEvent

    def on_key_press(self,event):
        try:
            if (self.declaration.key_code and event.key()==self.declaration.key_code) or \
                (self.declaration.key and self.declaration.key in event.text()):
                if not self.declaration.repeats and event.isAutoRepeat():
                    return
                self.declaration.pressed(event)
        finally:
            self._keyPressEvent(event)

    def on_key_release(self,event):
        try:
            if (self.declaration.key_code and event.key()==self.declaration.key_code) or \
                (self.declaration.key and self.declaration.key in event.text()):
                if not self.declaration.repeats and event.isAutoRepeat():
                    return
                self.declaration.released(event)
        finally:
            self._keyReleaseEvent(event)

def key_event_factory():
    return QtKeyEvent
# Inject the factory 
from enaml.qt.qt_factories import QT_FACTORIES
QT_FACTORIES['KeyEvent'] = key_event_factory

In your enaml file import the KeyEvent and use like this:

WidgetToWatchKeys:
  KeyEvent:
    key_code = QtCore.Qt.Key.Key_Up
    pressed :: model.move_up()

  KeyEvent:
    key_code = QtCore.Qt.Key.Key_Left
    pressed :: model.move_left()

  KeyEvent:
    key = 'x'
    pressed :: model.fire()
  #: etc...

Probably not the best way to do it but it worked fine for my needs.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top