enthought / mayavi

3D visualization of scientific data in Python
http://docs.enthought.com/mayavi/mayavi/
Other
1.3k stars 285 forks source link

Traceback when interacting with scene, keeping a key pressed #281

Closed pberkes closed 8 years ago

pberkes commented 8 years ago

On a slower system, like a Linux Virtual Machine, I observed an occasional traceback when pressing a key (say, z) and rotating the scene with the mouse:

Traceback (most recent call last):
  File "/home/berkes/Enthought/Canopy_64bit/System/lib/python2.7/site-packages/tvtk/pyface/ui/qt4/scene.py", line 198, in keyPressEvent
    QVTKRenderWindowInteractor.keyPressEvent(self, e)
  File "/home/berkes/Enthought/Canopy_64bit/System/lib/python2.7/site-packages/tvtk/pyface/ui/qt4/QVTKRenderWindowInteractor.py", line 418, in keyPressEvent
    ctrl, shift, key, 0, key_sym)
TypeError: SetEventInformationFlipY argument 5: a string of length 1 is required

The error is caused by the key argument sent to SetEventInformationFlipY being something like "zzzzz", i.e. multiple repetitions of the key I am pressing.

Using advanced debugging techniques, I edited the keyPressEvent method of tvtk.pyface.ui.qt4.QVTKRenderWindowInteractor like this:

    def keyPressEvent(self, ev):
        ctrl, shift = self._GetCtrlShift(ev)
        key_sym = self._KEY_MAP.get(ev.key(), None)
        if ev.key() < 256:
            # Sometimes, the OS allows a chord (e.g. Alt-T) to generate
            # a Unicode character outside of the 8-bit Latin-1 range. We will
            # try to pass along Latin-1 characters unchanged, since VTK expects
            # a single `char` byte. If not, we will try to pass on the root key
            # of the chord (e.g. 'T' above).
            if ev.text() and ev.text() <= u'\u00ff':
        print '1'
                key = ev.text().encode('latin-1')
            else:
        print '2'
                # Has modifiers, but an ASCII key code.
                key = chr(ev.key())
        else:
        print '3'
            key = chr(0)

    print '-', self.__saveX, self.__saveY, ctrl, shift, key, 0, key_sym, '-'
        self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY,
                                            ctrl, shift, key, 0, key_sym)
        self._Iren.KeyPressEvent()
        self._Iren.CharEvent()

While interacting with the scene, this is what I get on stdout:

[...]
1
- 241 465 False False z 0 None -
1
- 239 467 False False z 0 None -
1
- 239 430 False False z 0 None -
1
- 239 334 False False zzzz 0 None -
Uncaught exception captured
Traceback (most recent call last):
  File "/home/berkes/Enthought/Canopy_64bit/System/lib/python2.7/site-packages/tvtk/pyface/ui/qt4/scene.py", line 198, in keyPressEvent
    QVTKRenderWindowInteractor.keyPressEvent(self, e)
  File "/home/berkes/Enthought/Canopy_64bit/System/lib/python2.7/site-packages/tvtk/pyface/ui/qt4/QVTKRenderWindowInteractor.py", line 418, in keyPressEvent
    ctrl, shift, key, 0, key_sym)
TypeError: SetEventInformationFlipY argument 5: a string of length 1 is required

So we always go through the key = ev.text().encode('latin-1') branch, and that might return a string with multiple characters.

Link to the source code: https://github.com/enthought/mayavi/blob/master/tvtk/pyface/ui/qt4/QVTKRenderWindowInteractor.py#L424

pberkes commented 8 years ago

It turns out the key events in this case are in "auto-repeat" mode, http://doc.qt.io/qt-4.8/qkeyevent.html#isAutoRepeat . In that case QKeyEvent.count() can be larger than one, and text() will return multiple characters.

jonathanrocher commented 8 years ago

With equally advanced debugging techniques :), it seems like isAutoRepeat() can be True even if the text() method returns only 1 character. So testing for that doesn't give a 1-to-1 correspondance to the multi-character situation. On the other hand, it is faster to test for that than to test that all characters are identical. The following hack seems to work on my machine:

if ev.isAutoRepeat():
            key = key[0]

Would that be an acceptable fix?

pberkes commented 8 years ago

@dmsurti what do you think of @jonathanrocher 's proposed fix?

dmsurti commented 8 years ago

@pberkes: Can you try with this implementation, I don't have a Linux VM right away to test. Had cleaned up all my VM's earlier due to non-SSD drive.

def keyPressEvent(self, ev):
        ctrl, shift = self._GetCtrlShift(ev)
        if ev.key() < 256:
            key = str(ev.text())
        else:
            key = chr(0)

        keySym = _qt_key_to_key_sym(ev.key())
        if shift and len(keySym) == 1 and keySym.isalpha():
            keySym = keySym.upper()

        self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY,
                                            ctrl, shift, key, 0, keySym)
        self._Iren.KeyPressEvent()
        self._Iren.CharEvent()
jonathanrocher commented 8 years ago

Hey @dmsurti . I just tested your version of the method but that doesn't seem to help with the initial problem on my VM. I get the same error about the length of key being more than 1 as reported in the initial description.

dmsurti commented 8 years ago

@jonathanrocher Thanks for testing my version of the method, which did not work!!! Your fix LGTM. You may send a PR for this fix. Thanks.

mli0603 commented 4 years ago

I found the program is still buggy when I hold a key pressed, same error as @pberkes reported, TypeError: SetEventInformationFlipY argument 5: a string of length 1 is required. I am using python3, Ubuntu 18.04 and mayavi 4.7.1 installed via pip. I added a print after getting the key as below.

if ev.isAutoRepeat():
    key = key[0]
print("key",key)

The output, for example, when I keep 'z' pressed and print out the key, is

key b'z'
key 122
TypeError: SetEventInformationFlipY argument 5: a string of length 1 is required

If I remove the key = key[0], the callback works as shown

key b'z'
key b'z'
key b'z'
...

I am not very familiar with the Qt, maybe something is wrong after python3 where byte and string becomes two different types. Does anyone have an idea why?

cvsindelar commented 1 year ago

I also ran into this problem despite the bug fix, same error as everyone mentions (TypeError: SetEventInformationFlipY argument 5: a string of length 1 is required). I don't know what changed to trigger it now (Python3?), but the fix is simple: convert the key to bytes code:

  if ev.isAutoRepeat():                                                                                           
        # key = key[0]                                                                                              
        key = bytes(key[0]) 

Cheers-