Search code examples
python-3.xpyqt5qtablewidgetqcombobox

How to disable Mouse Wheel event of Qcombobox placed in QTableWidget in Python?


I have QTablewidget by QtDesigner, in which I have added Qcombobox in one of the columns. I have also added event filter to disable mouse wheel events, but it only works for the recently added combo box. How can I make it work for all the comboboxes in the table column?

what am I missing here?

Thanks.

Code by QtDesigner:

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(504, 346)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
        self.tableWidget.setGeometry(QtCore.QRect(10, 10, 481, 301))
        self.tableWidget.setRowCount(4)
        self.tableWidget.setColumnCount(3)
        self.tableWidget.setObjectName("tableWidget")
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(0, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(1, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(2, item)
        self.tableWidget.verticalHeader().setVisible(False)
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        item = self.tableWidget.horizontalHeaderItem(0)
        item.setText(_translate("MainWindow", "Name"))
        item = self.tableWidget.horizontalHeaderItem(1)
        item.setText(_translate("MainWindow", "Sex"))
        item = self.tableWidget.horizontalHeaderItem(2)
        item.setText(_translate("MainWindow", "Age"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

My Script:

from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QTableWidgetItem

from demoui import Ui_MainWindow

class democode(QtWidgets.QMainWindow, Ui_MainWindow):                 
    def __init__(self):
        super(democode, self).__init__()
        self.setupUi(self) 

        #Adding Data
        for row in range(0,4):
            for column in range(0,3):
                if column == 1:
                    self.combo = QtWidgets.QComboBox()
                    self.combo.addItem("Male")
                    self.combo.addItem("Female")
                    self.combo.installEventFilter(self)
                    self.tableWidget.setCellWidget(row, column, self.combo)
                else:
                    item = QTableWidgetItem('')
                    self.tableWidget.setItem(row, column, item)

    #Event Filter
    def eventFilter(self, source, event):
        if (event.type() == QtCore.QEvent.Wheel and
            source is self.combo):
            return True
        return super(democode, self).eventFilter(source, event)

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = democode()   
    window.show()
    sys.exit(app.exec_())

Solution

  • You are making a mistake similar to the one I warned you about in this answer:

    don't set instance attributes if they are not required: all those self.rowPosition, self.monthList, etc, change everytime the for loop cycles

    Everytime you do this:

    self.combo = QtWidgets.QComboBox()
    

    you are overwriting the combo attribute of the instance (self); this means that the last combo assigned to that attribute will be what self.combo refers to, and that's why it works for the latest combo only.

    What you are doing is practically this:

    self.combo = QtWidgets.QComboBox()
    self.combo = QtWidgets.QComboBox()
    self.combo = QtWidgets.QComboBox()
    self.combo = QtWidgets.QComboBox()
    # ...
    

    Think about it: what combo will refer self.combo to? Obviously, the last one.

    In simple cases like yours, for which you're only adding a limited set of widgets to the event filter, using isinstance() might be enough:

        def eventFilter(self, source, event):
            if (event.type() == QtCore.QEvent.Wheel and
                isinstance(source, QtWidgets.QComboBox)):
                return True
            return super(democode, self).eventFilter(source, event)
    

    But this will also mean that the filter will match any QCombobox for which you've installed the event filter on. If, for any other reason, you need to install a filter on another combo that is not one in that table, the wheel event will not be received by that combo too.

    The most easy and probably correct way to do that is to add those combo to a list, and check in the filter if the source is a member of that list:

    class democode(QtWidgets.QMainWindow, Ui_MainWindow):                 
        def __init__(self):
            super(democode, self).__init__()
            self.setupUi(self) 
    
            self.noWheelCombos = []
            for row in range(0,4):
                for column in range(0,3):
                    if column == 1:
                        # note that I'm *NOT* using "self"
                        combo = QtWidgets.QComboBox()
                        self.noWheelCombos.append(combo)
                        combo.addItem("Male")
                        combo.addItem("Female")
                        combo.installEventFilter(self)
                        self.tableWidget.setCellWidget(row, column, combo)
                    # ...
    
        def eventFilter(self, source, event):
            if (event.type() == QtCore.QEvent.Wheel and
                source in self.noWheelCombos):
                return True
            return super(democode, self).eventFilter(source, event)
    

    Another possibility is to override the wheelEvent with a function that does nothing. This is almost the same as subclassing and overwriting the wheelEvent method with a simple pass; it might not be very elegant, but works fine for very simple situations like this is:

            combo = QtWidgets.QComboBox()
            combo.wheelEvent = lambda event: None
    

    Finally, a small suggestion: if you always have the same elements for multiple combos, you can create a list outside the for cycle and use addItems() instead of adding single items everytime:

        items = "Male", "Female"
        for row in range(0,4):
            for column in range(0,3):
                if column == 1:
                    combo = QtWidgets.QComboBox()
                    combo.addItems(items)
                    # ...