Issue 277: MTVKeyboard - add_custom_layout doesn't work
Status:  Invalid
Owner:
Closed:  Jul 2010
Reported by linuxm...@googlemail.com, Jul 4, 2010
What steps will reproduce the problem?
1. Create your own KeyboardLayout.
2. Add it with add_custom_layout(Layout())
3. Start the Keyboard with MTVKeyboard(layout = 'Layout.ID')

What is the expected output? 
The Keyboard with my layout.
What do you see instead?
Traceback (most recent call last):
  File "start.py", line 91, in <module>
    pymt.runTouchApp(baseWidget)        
  File "/usr/lib/python2.6/site-packages/pymt/base.py", line 352, in runTouchApp
    pymt_window.mainloop()
  File "/usr/lib/python2.6/site-packages/pymt/ui/window/win_pygame.py", line 124, in mainloop
    evloop.idle()
  File "/usr/lib/python2.6/site-packages/pymt/base.py", line 201, in idle
    pymt_window.dispatch_event('on_draw')
  File "/usr/lib/python2.6/site-packages/pymt/event.py", line 344, in dispatch_event
    if func(*args):
  File "/usr/lib/python2.6/site-packages/pymt/ui/window/__init__.py", line 355, in on_draw
    w.dispatch_event('on_draw')
  File "/usr/lib/python2.6/site-packages/pymt/event.py", line 344, in dispatch_event
    if func(*args):
  File "/usr/lib/python2.6/site-packages/pymt/ui/widgets/widget.py", line 360, in on_draw
    w.dispatch_event('on_draw')
  File "/usr/lib/python2.6/site-packages/pymt/event.py", line 344, in dispatch_event
    if func(*args):
  File "/usr/lib/python2.6/site-packages/pymt/ui/widgets/scatter.py", line 141, in on_draw
    super(MTScatterWidget, self).on_draw()
  File "/usr/lib/python2.6/site-packages/pymt/ui/widgets/widget.py", line 357, in on_draw
    self.draw()
  File "/usr/lib/python2.6/site-packages/pymt/ui/widgets/composed/vkeyboard.py", line 433, in draw
    self._update()
  File "/usr/lib/python2.6/site-packages/pymt/ui/widgets/composed/vkeyboard.py", line 335, in _update
    layoutmode = '%s:%s' % (self.layout.ID, self.mode)
AttributeError: 'str' object has no attribute 'ID'

What version of the product are you using? On what operating system?
I use PyMT 0.4 but I saw the bug in the git too.
I use Archlinux

Please provide any additional information below.
This error came because of this line in pymt/ui/widgets/composed/vkeyboard.py
self.layout             = kwargs.get('layout')
You shuffle things because you use the same variable for the object and the id for the layout.
Replace:
        # read default layout in configuration
        if self.layout is None:
            idlayout = pymt.pymt_config.get('keyboard', 'layout')
            # search layout
            for layout in MTVKeyboard.available_layout:
                if layout.ID == idlayout:
                    self.layout = layout()
                    break
            # no layout found ?
            if self.layout is None:
                pymt.pymt_logger.warning('Vkeyboard: Keyboard layout <%s> not found, fallback on QWERTY' % idlayout)
                self.layout = KeyboardLayoutQWERTY()
With:
        layout_id = self.layout
        self.layout = None
        
        # read default layout in configuration
        if layout_id == None:
            layout_id = pymt.pymt_config.get('keyboard', 'layout')
        # search layout
        for layout in MTVKeyboard.available_layout:
            if layout.ID == layout_id:
                self.layout = layout
                break
        # no layout found ?
        if self.layout == None:
            pymt.pymt_logger.warning('Vkeyboard: Keyboard layout <%s> not found, fallback on QWERTY' % layout_id)
            self.layout = KeyboardLayoutQWERTY()
And you will see that it works.

Jul 4, 2010
Project Member #1 dennd...@gmail.com
Thanks for reporting the bug!

Can I get the layout to test it?
Owner: dennda85
Jul 4, 2010
#2 linuxm...@googlemail.com
I copied one of the standard layouts and changed only the header information.
Here is it:
class KeyboardLayoutQWERTZ(KeyboardLayout):
    '''Implementation of QWERTZ Layout'''
    ID              = 'qwertz'
    TITLE           = 'Qwertz'
    DESCRIPTION     = 'A classical German Keyboard'
    # Need to be comment out because I don't defined kbdlayout_default_font
    #FONT_FILENAME   = kbdlayout_default_font
    NORMAL_1 = [
        ('~', '~', None, 1),    ('!', '!', None, 1),    ('@', '@', None, 1),
        ('#', '#', None, 1),    ('$', '$', None, 1),    ('%', '%', None, 1),
        ('^', '^', None, 1),    ('&', '&', None, 1),    ('*', '*', None, 1),
        ('(', '(', None, 1),    (')', ')', None, 1),    ('_', '_', None, 1),
        ('+', '+', None, 1),    (u'\u232b', None, 'backspace', 2),
    ]
    NORMAL_2 = [
        (u'\u21B9', chr(0x09), None, 1.5),  ('q', 'q', None, 1),    ('w', 'w', None, 1),
        ('e', 'e', None, 1),    ('r', 'r', None, 1),    ('t', 't', None, 1),
        ('y', 'y', None, 1),    ('u', 'u', None, 1),    ('i', 'i', None, 1),
        ('o', 'o', None, 1),    ('p', 'p', None, 1),    ('{', '{', None, 1),
        ('}', '}', None, 1),    ('|', '|', None, 1.5)
    ]
    NORMAL_3 = [
        (u'\u21ea', None, 'capslock', 1.8),  ('a', 'a', None, 1),    ('s', 's', None, 1),
        ('d', 'd', None, 1),    ('f', 'f', None, 1),    ('g', 'g', None, 1),
        ('h', 'h', None, 1),    ('j', 'j', None, 1),    ('k', 'k', None, 1),
        ('l', 'l', None, 1),    (':', ':', None, 1),    ('"', '"', None, 1),
        (u'\u23ce', None, 'enter', 2.2),
    ]
    NORMAL_4 = [
        (u'\u21e7', None, 'shift_L', 2.5),  ('z', 'z', None, 1),    ('x', 'x', None, 1),
        ('c', 'c', None, 1),    ('v', 'v', None, 1),    ('b', 'b', None, 1),
        ('n', 'n', None, 1),    ('m', 'm', None, 1),    ('<', '<', None, 1),
        ('>', '>', None, 1),    ('?', '?', None, 1),    (u'\u21e7', None, 'shift_R', 2.5),
    ]
    NORMAL_5 = [
        ('', ' ', None, 12), (u'\u2b12', None, 'layout', 1.5), (u'\u2a2f', None, 'escape', 1.5),

    ]
    SHIFT_1 = [
        ('`', '`', None, 1),    ('1', '1', None, 1),    ('2', '2', None, 1),
        ('3', '3', None, 1),    ('4', '4', None, 1),    ('5', '5', None, 1),
        ('6', '6', None, 1),    ('7', '7', None, 1),    ('8', '8', None, 1),
        ('9', '9', None, 1),    ('0', '0', None, 1),    ('+', '+', None, 1),
        ('=', '=', None, 1),    (u'\u232b', None, 'backspace', 2),
    ]
    SHIFT_2 = [
        (u'\u21B9', chr(0x09), None, 1.5),  ('Q', 'Q', None, 1),    ('W', 'W', None, 1),
        ('E', 'E', None, 1),    ('R', 'R', None, 1),    ('T', 'T', None, 1),
        ('Y', 'Y', None, 1),    ('U', 'U', None, 1),    ('I', 'I', None, 1),
        ('O', 'O', None, 1),    ('P', 'P', None, 1),    ('[', '[', None, 1),
        (']', ']', None, 1),    ('?', '?', None, 1.5)
    ]
    SHIFT_3 = [
        (u'\u21ea', None, 'capslock', 1.8),  ('A', 'A', None, 1),    ('S', 'S', None, 1),
        ('D', 'D', None, 1),    ('F', 'F', None, 1),    ('G', 'G', None, 1),
        ('H', 'H', None, 1),    ('J', 'J', None, 1),    ('K', 'K', None, 1),
        ('L', 'L', None, 1),    (':', ':', None, 1),    ('"', '"', None, 1),
        (u'\u23ce', None, 'enter', 2.2),
    ]
    SHIFT_4 = [
        (u'\u21e7', None, 'shift_L', 2.5),  ('Z', 'Z', None, 1),    ('X', 'X', None, 1),
        ('C', 'C', None, 1),    ('V', 'V', None, 1),    ('B', 'B', None, 1),
        ('N', 'N', None, 1),    ('M', 'M', None, 1),    (',', ',', None, 1),
        ('.', '.', None, 1),    ('/', '/', None, 1),    (u'\u21e7', None, 'shift_R', 2.5),
    ]
    SHIFT_5 = [
        ('', ' ', None, 12), (u'\u2b12', None, 'layout', 1.5), (u'\u2a2f', None, 'escape', 1.5),
    ]
Jul 4, 2010
Project Member #3 dennd...@gmail.com
Sorry, I forgot to ask you for the code that you were using to add your own layout.
Can you paste it as well please?
Jul 4, 2010
#4 linuxm...@googlemail.com
Here is the whole code I used:
#!/usr/bin/env python

from pymt import *

class BaseWidget(MTWidget):
    def __init__(self):
        MTWidget.__init__(self)

class KeyboardLayoutQWERTZ(KeyboardLayout):
    '''Implementation of QWERTZ Layout'''
    ID              = 'qwertz'
    TITLE           = 'Qwertz'
    DESCRIPTION     = 'A classical German Keyboard'
    #FONT_FILENAME   = kbdlayout_default_font
    NORMAL_1 = [
        ('~', '~', None, 1),    ('!', '!', None, 1),    ('@', '@', None, 1),
        ('#', '#', None, 1),    ('$', '$', None, 1),    ('%', '%', None, 1),
        ('^', '^', None, 1),    ('&', '&', None, 1),    ('*', '*', None, 1),
        ('(', '(', None, 1),    (')', ')', None, 1),    ('_', '_', None, 1),
        ('+', '+', None, 1),    (u'\u232b', None, 'backspace', 2),
    ]
    NORMAL_2 = [
        (u'\u21B9', chr(0x09), None, 1.5),  ('q', 'q', None, 1),    ('w', 'w', None, 1),
        ('e', 'e', None, 1),    ('r', 'r', None, 1),    ('t', 't', None, 1),
        ('y', 'y', None, 1),    ('u', 'u', None, 1),    ('i', 'i', None, 1),
        ('o', 'o', None, 1),    ('p', 'p', None, 1),    ('{', '{', None, 1),
        ('}', '}', None, 1),    ('|', '|', None, 1.5)
    ]
    NORMAL_3 = [
        (u'\u21ea', None, 'capslock', 1.8),  ('a', 'a', None, 1),    ('s', 's', None, 1),
        ('d', 'd', None, 1),    ('f', 'f', None, 1),    ('g', 'g', None, 1),
        ('h', 'h', None, 1),    ('j', 'j', None, 1),    ('k', 'k', None, 1),
        ('l', 'l', None, 1),    (':', ':', None, 1),    ('"', '"', None, 1),
        (u'\u23ce', None, 'enter', 2.2),
    ]
    NORMAL_4 = [
        (u'\u21e7', None, 'shift_L', 2.5),  ('z', 'z', None, 1),    ('x', 'x', None, 1),
        ('c', 'c', None, 1),    ('v', 'v', None, 1),    ('b', 'b', None, 1),
        ('n', 'n', None, 1),    ('m', 'm', None, 1),    ('<', '<', None, 1),
        ('>', '>', None, 1),    ('?', '?', None, 1),    (u'\u21e7', None, 'shift_R', 2.5),
    ]
    NORMAL_5 = [
        ('', ' ', None, 12), (u'\u2b12', None, 'layout', 1.5), (u'\u2a2f', None, 'escape', 1.5),

    ]
    SHIFT_1 = [
        ('`', '`', None, 1),    ('1', '1', None, 1),    ('2', '2', None, 1),
        ('3', '3', None, 1),    ('4', '4', None, 1),    ('5', '5', None, 1),
        ('6', '6', None, 1),    ('7', '7', None, 1),    ('8', '8', None, 1),
        ('9', '9', None, 1),    ('0', '0', None, 1),    ('+', '+', None, 1),
        ('=', '=', None, 1),    (u'\u232b', None, 'backspace', 2),
    ]
    SHIFT_2 = [
        (u'\u21B9', chr(0x09), None, 1.5),  ('Q', 'Q', None, 1),    ('W', 'W', None, 1),
        ('E', 'E', None, 1),    ('R', 'R', None, 1),    ('T', 'T', None, 1),
        ('Y', 'Y', None, 1),    ('U', 'U', None, 1),    ('I', 'I', None, 1),
        ('O', 'O', None, 1),    ('P', 'P', None, 1),    ('[', '[', None, 1),
        (']', ']', None, 1),    ('?', '?', None, 1.5)
    ]
    SHIFT_3 = [
        (u'\u21ea', None, 'capslock', 1.8),  ('A', 'A', None, 1),    ('S', 'S', None, 1),
        ('D', 'D', None, 1),    ('F', 'F', None, 1),    ('G', 'G', None, 1),
        ('H', 'H', None, 1),    ('J', 'J', None, 1),    ('K', 'K', None, 1),
        ('L', 'L', None, 1),    (':', ':', None, 1),    ('"', '"', None, 1),
        (u'\u23ce', None, 'enter', 2.2),
    ]
    SHIFT_4 = [
        (u'\u21e7', None, 'shift_L', 2.5),  ('Z', 'Z', None, 1),    ('X', 'X', None, 1),
        ('C', 'C', None, 1),    ('V', 'V', None, 1),    ('B', 'B', None, 1),
        ('N', 'N', None, 1),    ('M', 'M', None, 1),    (',', ',', None, 1),
        ('.', '.', None, 1),    ('/', '/', None, 1),    (u'\u21e7', None, 'shift_R', 2.5),
    ]
    SHIFT_5 = [
        ('', ' ', None, 12), (u'\u2b12', None, 'layout', 1.5), (u'\u2a2f', None, 'escape', 1.5),
    ]


    
if __name__ == '__main__':
    main_window = getWindow()
    main_widget = BaseWidget()
    
    MTVKeyboard.add_custom_layout(KeyboardLayoutQWERTZ())
    keyboard = MTVKeyboard(layout = 'qwertz')

    main_widget.add_widget(MTScatterWidget())
    main_widget.add_widget(keyboard)
    
    runTouchApp(main_widget)

I have detected an other bug when you use add_custom_layout.
Try this code and then fix the bug with the code I gave you.
Now you custom layout works but then try to change the layout to QWERTY.
This works fine too.
But when you want to change back to QWERTZ you get the following message:
Traceback (most recent call last):
  File "test.py", line 89, in <module>
    runTouchApp(main_widget)        
  File "/usr/lib/python2.6/site-packages/pymt/base.py", line 352, in runTouchApp
    pymt_window.mainloop()
  File "/usr/lib/python2.6/site-packages/pymt/ui/window/win_pygame.py", line 124, in mainloop
    evloop.idle()
  File "/usr/lib/python2.6/site-packages/pymt/base.py", line 196, in idle
    self.dispatch_input()
  File "/usr/lib/python2.6/site-packages/pymt/base.py", line 181, in dispatch_input
    self.post_dispatch_input(type=type, touch=touch)
  File "/usr/lib/python2.6/site-packages/pymt/base.py", line 151, in post_dispatch_input
    wid.dispatch_event('on_touch_up', touch)
  File "/usr/lib/python2.6/site-packages/pymt/event.py", line 349, in dispatch_event
    event_type, args, getattr(self, event_type))
  File "/usr/lib/python2.6/site-packages/pymt/event.py", line 344, in dispatch_event
    if func(*args):
  File "/usr/lib/python2.6/site-packages/pymt/ui/widgets/composed/kineticlist.py", line 425, in on_touch_up
    child.dispatch_event('on_touch_down', touch)
  File "/usr/lib/python2.6/site-packages/pymt/event.py", line 349, in dispatch_event
    event_type, args, getattr(self, event_type))
  File "/usr/lib/python2.6/site-packages/pymt/event.py", line 344, in dispatch_event
    if func(*args):
  File "/usr/lib/python2.6/site-packages/pymt/ui/widgets/button.py", line 153, in on_touch_down
    self.dispatch_event('on_press', touch)
  File "/usr/lib/python2.6/site-packages/pymt/event.py", line 337, in dispatch_event
    self._raise_dispatch_exception(event_type, args, handler)
  File "/usr/lib/python2.6/site-packages/pymt/event.py", line 334, in dispatch_event
    if handler(*args):
  File "/usr/lib/python2.6/site-packages/pymt/utils.py", line 32, in call_fn
    return fn(*(cargs + fargs), **d)
  File "/usr/lib/python2.6/site-packages/pymt/ui/widgets/composed/vkeyboard.py", line 432, in on_layout_change
    self.layout = layout()
TypeError: 'KeyboardLayoutQWERTZ' object is not callable

       
Jul 4, 2010
Project Member #5 dennd...@gmail.com
OK, there is no bug. :-)
You're using the API incorrectly.
a) add_custom_layout expects a class object, not an object of that class. I.e., don't call add_custom_layout(KeyboardLayoutQWERTZ()), but add_custom_layout(KeyboardLayoutQWERTZ).
Then you can just create a MTVKeyboard (even without the layout= parameter) and choose the qwertz layout from the list when you click the button on the bottom left.

b) If you want to give the keyboard the right layout from the start, don't pass an ID but the actual keyboardlayout object. I.e.:
k = MTVKeyboard(layout=KeyboardLayoutQWERTZ())
instead of:
k = MTVKeyboard(layout='qwertz').

That both works for me.
(You are missing a SIZE attribute in your layout. I think you can just copy that one line from the QWERTY layout.)

That should help.
I'm marking this issue as invalid since I don't think it's a bug. If you disagree, feel free to post again.
Thanks for taking the time to report this issue, nevertheless!
Status: Invalid
Jul 4, 2010
#6 linuxm...@googlemail.com
OK, I became a little bit confused because of the config file were it says layout = qwerty.
Thank you for these informations and sorry that I waste your time.

(You are missing a SIZE attribute in your layout. I think you can just copy that one line from the QWERTY layout.)
There is no SIZE attribute in the QWERTY layout - I think that's because I don't use the git version.