pythonpyqtscikit-imageeventfilter

disable arrow keys in Slider


When using scikit CollectionViewer (simple image browser) I'd like pressing arrow keys not to trigger going to prev/next image after slider got focus. I used eventFilter for that

from skimage.viewer import ImageViewer
from skimage.viewer.qt import Qt
from skimage.viewer.widgets import Slider

class SilentViewer(ImageViewer): #CollectionViewer with some modifications

    def __init__(self, image_collection, update_on='move', **kwargs):
        self.image_collection = image_collection
        self.index = 0
        self.num_images = len(self.image_collection)

        first_image = image_collection[0]
        super(SilentViewer, self).__init__(first_image)

        slider_kws = dict(value=0, low=0, high=self.num_images - 1)
        slider_kws['update_on'] = update_on
        slider_kws['callback'] = self.update_index
        slider_kws['value_type'] = 'int'
        self.slider = Slider('frame', **slider_kws)
        self.layout.addWidget(self.slider)
        self.installEventFilter(self) #Modification to CollectionViewer №1

    def update_index(self, name, index):
        index = int(round(index))

        if index == self.index:
            return

        index = max(index, 0)
        index = min(index, self.num_images - 1)

        self.index = index
        self.slider.val = index
        self.update_image(self.image_collection[index])

    def eventFilter(self,obj,evt): #Modification to CollectionViewer №2
        try:
            print(evt.type(), evt.key(), evt.text())
            if (evt.key() == Qt.Key_Left or
                evt.key() == Qt.Key_Right or
                evt.key() == Qt.Key_Up or
                evt.key() == Qt.Key_Down):

                print("Ignored arrow button")
                return True
            else:
                return False
        except:
            print("Smth went wrong")
            return False

#for testing reasons
from skimage import data
from skimage.transform import pyramid_gaussian

img = data.coins()
img_pyr = pyramid_gaussian(img, downscale=2, multichannel=False)
img_collection = tuple(img_pyr)
viewer = SilentViewer(img_collection)
viewer.show()

event filter seems to be working: key presses and other events trigger console output. However, arrow keys trigger image change. If I change to update_on='release' (see init), arrow keys do not trigger the image change, but make slider position change. Could you please tell how can I make the arrow presses to be full consumed by the filter?


Solution

  • Analyzing the Slider source code, it can be seen that it is a container, that is, a widget that has other widgets (QLabel, QSlider and QLineEdit), so the filter must be installed on the internal QSlider and not on the container.

    from skimage.viewer import ImageViewer
    from skimage.viewer.qt import QtCore
    from skimage.viewer.widgets import Slider
    
    
    class SilentViewer(ImageViewer):  # CollectionViewer with some modifications
        def __init__(self, image_collection, update_on="move", **kwargs):
            self.image_collection = image_collection
            self.index = 0
            self.num_images = len(self.image_collection)
    
            print(self.num_images)
    
            first_image = image_collection[0]
            super(SilentViewer, self).__init__(first_image)
    
            slider_kws = dict(value=0, low=0, high=self.num_images - 1)
            slider_kws["update_on"] = update_on
            slider_kws["callback"] = self.update_index
            slider_kws["value_type"] = "int"
            self.slider = Slider("frame", **slider_kws)
            self.layout.addWidget(self.slider)
    
            self.slider.slider.installEventFilter(self)
    
        def update_index(self, name, index):
            if index == self.index:
                return
            index = max(index, 0)
            index = min(index, self.num_images - 1)
            self.index = index
            self.update_image(self.image_collection[index])
    
        def eventFilter(self, obj, evt):
            if obj is self.slider.slider and evt.type() == QtCore.QEvent.KeyPress:
                if evt.key() in (
                    QtCore.Qt.Key_Left,
                    QtCore.Qt.Key_Right,
                    QtCore.Qt.Key_Up,
                    QtCore.Qt.Key_Down,
                ):
                    return True
            return super(SilentViewer, self).eventFilter(obj, evt)
    
    
    def main():
        # for testing reasons
        from skimage import data
        from skimage.transform import pyramid_gaussian
    
        img = data.coins()
        img_pyr = pyramid_gaussian(img, downscale=2, multichannel=False)
        img_collection = tuple(img_pyr)
        viewer = SilentViewer(img_collection)
        viewer.show()
    
    
    if __name__ == "__main__":
        main()