1

[I'm using PyQt4, but I think this Qt4 issue is not Python specific.]

I have a QTableWidget. In each row, the first column holds a button. When clicked, the row is removed.

To remove the row, I use removeRow(int row) method, which takes as argument the index of the row. When connecting the signal, I can't know the index of the row because it might change in the meantime (for instance if the first row is removed, all row indexes are changed).

The accepted answer here suggests to pass the callback an instance of a QTableWidgetItem in the line, then get the row number from this item at deletion time.

This would be nice, except none of the elements of the row is a QTableWidgetItem. The elements are the button itself and a few ComboBoxes.

I can't figure out a way around this.

Can I somehow fit one of my elements into a QTableWidgetItem? Should I add a QTableWidgetItem in some sort of hidden column?

Our current implementation uses indexAt(QtGui.qApp.focusWidget()) (see other answer to question mentioned above), which looks like a sorry workaround to me.

If I replace the button with a checkable QTableWidgetItem like this

rm_item = QtGui.QTableWidgetItem()
rm_item.setFlags(QtCore.Qt.ItemIsUserCheckable |
                 QtCore.Qt.ItemIsEnabled)

I have a QTableWidgetItem I can use to get back to the row index. But I don't know how to catch a "checked" or "clicked" event from it like I do with the button. All I found is the itemClicked signal of QTableWidget, but then I'd have to filter all the other widgets out.

There has to be something obvious I'm missing.

Edit

From what I read here, I could add both a QTableWidgetItem with setItem and a Button widget with setCellWidget to the same cell. This doesn't seem so natural to me, but apparently it works (can't test right now).

I guess I'll do that. Add the Button, plus a dummy QTableWidgetItem on the same cell to pass as a reference to the row.

Is this how it is meant to be?

Edit 2

Or maybe QTableWidget is not the proper Widget and I should be using a Layout, as suggested here.

Community
  • 1
  • 1
Jérôme
  • 13,328
  • 7
  • 56
  • 106
  • This is not a python answer, but I think my C++ answer [here](http://stackoverflow.com/a/29177041/616644) may be helpful. – Rick Smith Apr 14 '15 at 21:15

2 Answers2

2

It seems that using a layout rather than a table is possibly the most "correct" answer, but that may come with it's own difficulties, as seen in my answer to this question:

If you want to continue using a table, a somewhat cleaner solution than adding dummy items would be to use a persistent model index:

            button = QtGui.QPushButton(text, self.table)
            self.table.setCellWidget(row, column, button)
            index = QtCore.QPersistentModelIndex(
                self.table.model().index(row, column))
            button.clicked.connect(
                lambda *args, index=index: self.handleButton(index))

    def handleButton(self, index):
        print('button clicked:', index.row())
        if index.isValid():
            self.table.removeRow(index.row())
Community
  • 1
  • 1
ekhumoro
  • 115,249
  • 20
  • 229
  • 336
  • Thanks again, @ekhumoro. Works a treat. I just used `button.clicked.connect(lambda: self.handleButton(index))` – Jérôme Apr 15 '15 at 08:54
0

If I understand your question correctly:

def set_button(self, row, col):
    # create a push button 
    btn = QtGui.QPushButton('Remove row')
    # connect to action
    btn.clicked.connect(self.remove_row)
    # set in cell 
    self.setCellWidget(row, col, btn)


def remove_row(self):
    # find what is clicked 
    clicked = QtGui.qApp.focusWidget()
    # position 
    idx = self.indexAt(clicked.pos())
    # remove this row
    self.removeRow(idx.row())
ngulam
  • 995
  • 1
  • 6
  • 11
  • This is what we do already, but I feel weird about using cursor location here. When connecting the signal, we know the row elements, so it is a bit twisted to lose this information deliberately and then seek cursor position afterwards. Besides, if the button is clicked from the keyboard, the method fails (see [accepted answer here](http://stackoverflow.com/questions/24649625/how-to-get-the-row-under-the-cursor-for-a-qtablewidget)). – Jérôme Apr 14 '15 at 19:01
  • Sorry, I misread the question. AFAIK you are right: with `table.itemclicked` you have to check the state of the checkboxes. I didn't find another way, by now. NB: keyboard interaction with the buttons is working correctly with me. – ngulam Apr 14 '15 at 19:57
  • I suppose if you click from the keyboard and the mouse pointer is on another line, `indexAt(QtGui.qApp.focusWidget())` will return the index of the other line. Anyway, see the edits I made to the question. I think I have an acceptable workaround (use both a QTableWidgetItem and setCellItem in the same cell) and an answer which is that I shouldn't be using a QTableWidget in the first place but a QLayout. – Jérôme Apr 14 '15 at 20:06