JPFrancoia
JPFrancoia

Reputation: 5629

QTableView intercepts Key_Down and Key_Up events

I'm trying to implement "things" on a QTableView. So, I started to override its keyPressEvent method, as it's described everywhere:

if e.key() == QtCore.Qt.Key_F2 and self.tableau.hasFocus():
    blabla

elif e.key() == QtCore.Qt.Key_Return and self.tableau.hasFocus():
    blabla

#TEST
elif e.key() == QtCore.Qt.Key_Right:
    print("right")
elif e.key() == QtCore.Qt.Key_Down:
    print("down")
else:
    print("waza")

I can handle the F2 and the Return events, they work. But not the Key_Down event ! The Key_right works as well, but not the Down. I think it's because QTableWidget "intercepts" some of the events. When normal letters are pressed, no event is "emitted" either, for example.

I would like to access all the events (or at least the Up and Down events), how can I do that ?

EDIT:

The view I use is basically a QTableView, which I modified a bit:

class ViewPerso(QtGui.QTableView):

    def __init__(self, parent=None):
        super(ViewPerso, self).__init__(parent)

        self.parent = parent


    def currentChanged(self, current, previous):

        index = current.sibling(current.row(), 4)

        try:
            if not index.data() or len(self.parent.vidsSelected()) > 1:
                return
        except AttributeError:
            pass

        self.clicked.emit(current)
        self.scrollTo(current)


    def keyboardSearch(self, search):

        pass

I do not use QTableWidget, it doesn't do what I want. I also subclassed the view delegate, but normally (normally), nothing should interfere.

Here is how I use my view: I load a model, a proxy, and then I use my view:

    self.modele = ModelPerso()

    self.modele.setEditStrategy(QtSql.QSqlTableModel.OnFieldChange)
    self.modele.setTable("videos")
    self.modele.select()

    self.proxy = QtGui.QSortFilterProxyModel()
    self.proxy.setSourceModel(self.modele)

    self.tableau = ViewPerso(self)
    self.tableau.setModel(self.proxy)
    self.tableau.setItemDelegate(ViewDelegate(self))

I don't know where the keypresses end up, for now, they just disappear. I think (I think), each widget has a way to treat keypresses event, and maybe QTableView ignores the keys up and down ?

Upvotes: 2

Views: 2986

Answers (3)

mike rodent
mike rodent

Reputation: 15642

There seems to be a bit of confusion in the question and in the answers. If you want to propagate key events (for example) from your QTableView up to its parent/ancestor widget(s), this should usually work:

def keyPressEvent(self, event):
    print(f'kpe TableView {event}')
    # super().keyPressEvent(event)
    self.parent().keyPressEvent(event)

... if you comment out the super() line there should be no need to do event.ignore() ... although certain components other than QTableView may handle things differently, particularly editors, I suspect.

Anyway, the next thing is to understand what the parent is and what happens to a KeyPressEvent in it. In my case the parent was a QFrame, positioned in a hierarchy of QWidgets/QLayouts. However I also have an event filter set up on the QMainWindow, i.e. right at the top of the hierarchy, like this:

class KeyPressEater(QtCore.QObject):
    def __init__(self, main_window):
        super().__init__()
        self.main_window = main_window

    def eventFilter(self, obj, event):
        if event.type() == QtCore.QEvent.KeyPress:
            if event.modifiers() == QtCore.Qt.NoModifier:
                if event.key() == QtCore.Qt.Key_Return:
                    self.main_window.entry_box.keyPressEvent(event)
        return super().eventFilter(obj, event)

In the QMainWindow constructor:

self.kpe = KeyPressEater(self)
self.installEventFilter(self.kpe)

And I confirm that the event in question (on pressing Return with focus on the QTableView) gets propagated, without any further explicit wiring, all the way up to the KeyPressEater installed on the QMainWindow, and is thus caught by it and fires the key press event on the entry_box as configured by this line:

self.main_window.entry_box.keyPressEvent(event)

Of course, you may not want all key press events to be propagated up from the table view: filtering should be easy enough.

Upvotes: 0

Bandhit Suksiri
Bandhit Suksiri

Reputation: 3450

For implemented outside class, You have to override this keyPressEvent method out side. Like this example:

import sys
from PyQt4 import QtCore, QtGui

class ViewPerso (QtGui.QTableView):
    def __init__(self, parent = None):
        super(ViewPerso, self).__init__(parent)
        self.parent = parent

    def currentChanged (self, current, previous):
        index = current.sibling(current.row(), 4)
        try:
            if not index.data() or len(self.parent.vidsSelected()) > 1:
                return
        except AttributeError:
            pass
        self.clicked.emit(current)
        self.scrollTo(current)

    def keyboardSearch (self, search):
        pass

class exampleQMainWindow (QtGui.QMainWindow):
    def __init__ (self):
        super(exampleQMainWindow, self).__init__()
        testQTableWidget = ViewPerso(self)
        self.setCentralWidget(testQTableWidget)
        testQTableWidget.keyPressEvent = self.keyPressEvent

    def keyPressEvent (self, eventQKeyEvent):
        key = eventQKeyEvent.key()
        if key == QtCore.Qt.Key_F1:
            print 'Help'
        elif key == QtCore.Qt.Key_F5:
            print 'Reload'
        elif key == QtCore.Qt.Key_Left:
            print 'Left'
        elif key == QtCore.Qt.Key_Up:
            print 'Up'
        elif key == QtCore.Qt.Key_Right:
            print 'Right'
        elif key == QtCore.Qt.Key_Down:
            print 'Down'

app = QtGui.QApplication([])
window = exampleQMainWindow()
window.show()
sys.exit(app.exec_())

Upvotes: 0

JPFrancoia
JPFrancoia

Reputation: 5629

Ok, I finally ended up doing this:

class ViewPerso (QtGui.QTableView):
    def __init__(self, parent = None):
        super(ViewPerso, self).__init__(parent)
        self.parent = parent

    def currentChanged (self, current, previous):
        index = current.sibling(current.row(), 4)
        try:
            if not index.data() or len(self.parent.vidsSelected()) > 1:
                return
        except AttributeError:
            pass
        self.clicked.emit(current)
        self.scrollTo(current)

    def keyboardSearch (self, search):
        pass

    def keyPressEvent (self, e):
        super(ViewPerso, self).keyPressEvent(e)
        e.ignore()

The event was probably stuck in ViewPerso, so by overriding its keyPressEvent method like this, I get the usual treatments QTableWidget does on the events, and I the event is propagated to the parent widget, so I can use it again.

I tried to do what you showed in your edit, but it simply does not work for me, I don't really know why. My program is also very big (~10k lines), so the reason could come from anywhere. But thanks to make me realize where the error came from.

Upvotes: 1

Related Questions