如何在 QTableWidget 列的每个单元格中显示一个按钮,以便在单击时删除其相应的行?
我想在 QTableWidget 列的每个单元格中显示一个按钮。单击每个按钮时,必须删除表中其相应的行。
为此,我创建了一个 RemoveRowDelegate
类,其中按钮作为编辑器,并使用 CustomTable
类中的 QAbstractItemView::openPersistentEditor
方法来显示按钮永久。
class RemoveRowDelegate(QStyledItemDelegate):
def __init__(self, parent, cross_icon_path):
super().__init__(parent)
self.cross_icon_path = cross_icon_path
self.table = None
def createEditor(self, parent, option, index):
editor = QToolButton(parent)
editor.setStyleSheet("background-color: rgba(255, 255, 255, 0);") # Delete borders but maintain the click animation (as opposed to "border: none;")
pixmap = QPixmap(self.cross_icon_path)
button_icon = QIcon(pixmap)
editor.setIcon(button_icon)
editor.clicked.connect(self.remove_row)
return editor
# Delete the corresponding row
def remove_row(self):
sending_button = self.sender()
for i in range(self.table.rowCount()):
if self.table.cellWidget(i, 0) == sending_button:
self.table.removeRow(i)
break
class CustomTable(QTableWidget):
def __init__(self, parent=None, df=None):
super().__init__(parent)
self.columns = []
self.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
self.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
if df is not None:
self.fill(df)
# Build the table from a pandas df
def fill(self, df):
self.columns = [''] + list(df.columns)
nb_rows, _ = df.shape
nb_columns = len(self.columns)
self.setRowCount(nb_rows)
self.setColumnCount(nb_columns)
self.setHorizontalHeaderLabels(self.columns)
for i in range(nb_rows):
self.openPersistentEditor(self.model().index(i, 0))
for j in range(1, nb_columns):
item = df.iloc[i, j-1]
table_item = QTableWidgetItem(item)
self.setItem(i, j, table_item)
def add_row(self):
nb_rows = self.rowCount()
self.insertRow(nb_rows)
self.openPersistentEditor(self.model().index(nb_rows, 0))
def setItemDelegateForColumn(self, column_index, delegate):
super().setItemDelegateForColumn(column_index, delegate)
delegate.table = self
我为表的第一列设置了委托,并从 pandas 数据帧构建后者:
self.table = CustomTable() # Here, self is my user interface
remove_row_delegate = RemoveRowDelegate(self, self.cross_icon_path)
self.table.setItemDelegateForColumn(0, remove_row_delegate)
self.table.fill(df)
目前,此解决方案可以完成这项工作,但我想到了其他几种可能性:
- 使用 QTableWidget: :setCellWidget 方法
- 覆盖
paint
方法并捕获左键单击事件
但是:
- 我相信第一个替代方案不是很干净,因为我必须在 for 循环中创建按钮并且每次一行添加了(但毕竟我也打电话
openPersistentEditor
与此处相同)。 - 我想知道第二种选择是否值得付出努力。如果是的话,该怎么做?
另外:
- 我相信我的
remove_row
方法可以在迭代所有行时进行优化(这就是我考虑第二种选择的原因之一)。您有更好的建议吗? - 我必须重写
setItemDelegateForColumn
方法,以便可以从RemoveRowDelegate
类访问该表。可以避免吗?
您认为可能感兴趣的任何其他评论将不胜感激!
I want to display a button in each cell of a QTableWidget
's column. Each button, when clicked, must remove its corresponding row in the table.
To do so, I created a RemoveRowDelegate
class with the button as editor and used the QAbstractItemView::openPersistentEditor
method in a CustomTable
class to display the button permanently.
class RemoveRowDelegate(QStyledItemDelegate):
def __init__(self, parent, cross_icon_path):
super().__init__(parent)
self.cross_icon_path = cross_icon_path
self.table = None
def createEditor(self, parent, option, index):
editor = QToolButton(parent)
editor.setStyleSheet("background-color: rgba(255, 255, 255, 0);") # Delete borders but maintain the click animation (as opposed to "border: none;")
pixmap = QPixmap(self.cross_icon_path)
button_icon = QIcon(pixmap)
editor.setIcon(button_icon)
editor.clicked.connect(self.remove_row)
return editor
# Delete the corresponding row
def remove_row(self):
sending_button = self.sender()
for i in range(self.table.rowCount()):
if self.table.cellWidget(i, 0) == sending_button:
self.table.removeRow(i)
break
class CustomTable(QTableWidget):
def __init__(self, parent=None, df=None):
super().__init__(parent)
self.columns = []
self.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
self.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
if df is not None:
self.fill(df)
# Build the table from a pandas df
def fill(self, df):
self.columns = [''] + list(df.columns)
nb_rows, _ = df.shape
nb_columns = len(self.columns)
self.setRowCount(nb_rows)
self.setColumnCount(nb_columns)
self.setHorizontalHeaderLabels(self.columns)
for i in range(nb_rows):
self.openPersistentEditor(self.model().index(i, 0))
for j in range(1, nb_columns):
item = df.iloc[i, j-1]
table_item = QTableWidgetItem(item)
self.setItem(i, j, table_item)
def add_row(self):
nb_rows = self.rowCount()
self.insertRow(nb_rows)
self.openPersistentEditor(self.model().index(nb_rows, 0))
def setItemDelegateForColumn(self, column_index, delegate):
super().setItemDelegateForColumn(column_index, delegate)
delegate.table = self
I set the delegate for the first column of the table and build the latter from a pandas
dataframe:
self.table = CustomTable() # Here, self is my user interface
remove_row_delegate = RemoveRowDelegate(self, self.cross_icon_path)
self.table.setItemDelegateForColumn(0, remove_row_delegate)
self.table.fill(df)
For now, this solution does the job but I think of several other possibilities:
- Using the
QTableWidget::setCellWidget
method - Overriding the
paint
method and catching the left click event
But:
- I believe the first alternative is not very clean as I must create the buttons in a for loop and each time a row is added (but after all, I also call
openPersistentEditor
the same way here). - I am wondering if the second alternative is worth the effort. And if it does, how to do it?
Also:
- I believe my
remove_row
method can be optimized as I iterate over all rows (that is one of the reasons why I thought about the second alternative). Would you have a better suggestion ? - I had to override the
setItemDelegateForColumn
method so that I can access the table from theRemoveRowDelegate
class. Can it be avoided ?
Any other remark that you think might be of interest would be greatly appreciated!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
根据@ekhumoro的建议,我最终使用了上下文菜单:
此外,请注意,使用
QTableWidget::removeRow
方法比我以前的方法更优化。由于QTableWidget::indexAt
方法,我们只需要从点击位置正确获取行索引即可。As suggested by @ekhumoro, I finally used a context menu:
Moreover, note that using
QTableWidget::removeRow
method is more optimized than my previous method. One just need to get the row index properly from the click position thanks toQTableWidget::indexAt
method.