Question

I have my push button that I set in qTableWidget column 9. It contains 3 values so when it's clicked it send signal with those values to my main that triggers DoSomething() method in my main. Problem is that this line consumes to much time self.tableWidget.setCellWidget(i,9, button) . Here is code for my push button, and PopulateTable() method with output of the problem:

class MyPushButton(QtGui.QPushButton):
    def __init__(self, value1, value2, value3, main):
        super(MyPushButton, self).__init__()
        self.__value1   = value1
        self.__value2   = value2
        self.__value3   = value3
        self.__main     = main
        self.connect(self, QtCore.SIGNAL('clicked()'), self, QtCore.SLOT("triggerOutput()"))

    @QtCore.pyqtSlot()
    def triggerOutput(self):
        self.__main.emit(QtCore.SIGNAL("DoSomethingSignal(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)"), self.__value1 , self.__value2 , self.__value3) 

PopulateTable() method in my class PYQTEmaszForm(QtGui.QMainWindow):

def PopulateTable(self, numberOfRows, rows):

EDIT 2 vvvvv

    self.tableWidget.setColumnCount(10)
    self.tableWidget.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
    self.tableWidget.setSelectionMode(QtGui.QAbstractItemView.NoSelection)
    self.tableWidget.setFocusPolicy(QtCore.Qt.NoFocus)
    self.tableWidget.verticalHeader().hide()
    self.tableWidget.setVerticalScrollMode(QtGui.QAbstractItemView.ScrollPerPixel)
#next line was the problem
    #self.tableWidget.verticalHeader().setResizeMode(QtGui.QHeaderView.ResizeToContents)
    self.tableWidget.horizontalHeader().setResizeMode(QtGui.QHeaderView.Fixed)
    self.tableWidget.resizeColumnsToContents()

^^^^^

    self.tableWidget.setVisible(0)
    self.tableWidget.setSortingEnabled(0)
    self.tableWidget.setRowCount(numberOfRows)

    for i in range(0,numberOfRows):
        #I set columns 0-8 here like this self.tableWidget.setItem(i, colNum, QtGui.QTableWidgetItem(rows[i][colNum]))  

        #here is my problem:
        button = MyPushButton(value1, value2, value3, self)# note that tose 3 values I can get only here, in a loop, so I must store them in my button that I use as link
        button.setText("Click to do something")
        startTime = time.time()
        self.tableWidget.setCellWidget(i,9, button)
        print i," self.tableWidget.setCellWidget(i,9, button) time: ", time.time() - startTime

    self.tableWidget.resizeColumnsToContents()
    self.tableWidget.setSortingEnabled(1)
    self.tableWidget.sortByColumn(0,QtCore.Qt.AscendingOrder)
    self.tableWidget.setVisible(1)

time to cetCellWidget grows for every line. Here is what i get for 325 rows:

0 self.tableWidget.setCellWidget(i,9, button) time:  0.010999917984
1 self.tableWidget.setCellWidget(i,9, button) time:  0.0119998455048
2 self.tableWidget.setCellWidget(i,9, button) time:  0.0130000114441
3 self.tableWidget.setCellWidget(i,9, button) time:  0.0150001049042
# ....
100 self.tableWidget.setCellWidget(i,9, button) time:  0.141999959946
101 self.tableWidget.setCellWidget(i,9, button) time:  0.134000062943
102 self.tableWidget.setCellWidget(i,9, button) time:  0.144999980927
103 self.tableWidget.setCellWidget(i,9, button) time:  0.151000022888
# ....
321 self.tableWidget.setCellWidget(i,9, button) time:  0.209000110626
322 self.tableWidget.setCellWidget(i,9, button) time:  0.211999893188
323 self.tableWidget.setCellWidget(i,9, button) time:  0.213000059128
324 self.tableWidget.setCellWidget(i,9, button) time:  0.212000131607

So populating table lasts over 1 minute.

My question is why setCellWidget needs so much time and why time is growing?

What am I doing wrong?

EDIT1 I've tried setting simple QPushButton like this: button = QtGui.QPushButton(), and there is no difference in time consumption of setCellWidget method. So it doesn't depends on MyPushButton widget.

Was it helpful?

Solution

The problem is that setCellWidget is just a convenience method: it is not optimized for adding hundreds of widgets to a single table, and so the slow-down in performance that you are seeing is inevitable.

To get the best performance, you will have to create a custom delegate and use QStyle to draw the button in its paint method. Of course, this also means you will have to implement all the mouse-handling and so forth yourself as well.

To give you an idea of what's involved, some example code can be found here.

EDIT:

Here is a test script based on the original code in the question. For me, this completes in a fraction of a second for 325 rows:

import time
from PyQt4 import QtCore, QtGui

class MyPushButton(QtGui.QPushButton):
    def __init__(self, value1, value2, value3, main):
        super(MyPushButton, self).__init__()
        self.__value1   = value1
        self.__value2   = value2
        self.__value3   = value3
        self.__main     = main
        self.connect(self, QtCore.SIGNAL('clicked()'), self, QtCore.SLOT("triggerOutput()"))

    @QtCore.pyqtSlot()
    def triggerOutput(self):
        self.__main.emit(QtCore.SIGNAL("DoSomethingSignal(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)"), self.__value1 , self.__value2 , self.__value3)

class Window(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.table = QtGui.QTableWidget(self)
        self.button = QtGui.QPushButton('Populate', self)
        self.button.clicked.connect(self.handleButton)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.table)
        layout.addWidget(self.button)

    def handleButton(self):
        self.populateTable(325)

    def populateTable(self, numberOfRows):
        self.table.setVisible(0)
        self.table.setSortingEnabled(0)
        self.table.setRowCount(numberOfRows)
        self.table.setColumnCount(10)

        for i in range(0, numberOfRows):
            for colNum in range(10):
                text = 'Text(%d, %d)' % (i, colNum)
                self.table.setItem(i, colNum, QtGui.QTableWidgetItem(text))
            button = MyPushButton(1, 2, 3, self)
            button.setText("Click")
            startTime = time.time()
            self.table.setCellWidget(i,9, button)
            print i," self.tableWidget.setCellWidget(i,9, button) time: ", time.time() - startTime

        self.table.resizeColumnsToContents()
        self.table.setSortingEnabled(1)
        self.table.sortByColumn(0, QtCore.Qt.AscendingOrder)
        self.table.setVisible(1)

if __name__ == '__main__':

    import sys
    app = QtGui.QApplication(sys.argv)
    window = Window()
    window.setGeometry(600, 300, 1000, 600)
    window.show()
    sys.exit(app.exec_())

UPDATE:

I'm not sure exactly what you want, but the row resizing issue can probably be resolved by setting the resize-mode to Fixed before populating the table, and then setting it to ResizeToContents afterwards:

    ...
    self.tableWidget.horizontalHeader().setResizeMode(QtGui.QHeaderView.Fixed)
    self.tableWidget.verticalHeader().setResizeMode(QtGui.QHeaderView.Fixed)

    # populate table

    self.table.verticalHeader().setResizeMode(QtGui.QHeaderView.ResizeToContents)
    self.table.resizeColumnsToContents()
    self.tableWidget.setSortingEnabled(1)
    self.tableWidget.sortByColumn(0, QtCore.Qt.AscendingOrder)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top