QTableWidget 和 setCellWidget:调整大小问题

3

我尝试在QTableWidget的某些单元格内插入一些QRadioButton。 情况类似于此帖子。 特别是,@eyllanesc的解决方案,使用PySide2如下所示

import sys

from PySide2.QtWidgets import QApplication, QTableWidget, QTableWidgetItem, \
    QButtonGroup, QRadioButton

app = QApplication(sys.argv)

searchView = QTableWidget(0, 4)
colsNames = ['A', 'B', 'C']
searchView.setHorizontalHeaderLabels(['DIR'] + colsNames)
dirNames = {'A': ['/tmp', '/tmp/dir1'], 'B': ['/tmp/dir2'],
            'C': ['/tmp/dir3']}

rowCount = sum(len(v) for (name, v) in dirNames.items())
searchView.setRowCount(rowCount)

index = 0

for letter, paths in dirNames.items():
    for path in paths:
        it = QTableWidgetItem(path)
        searchView.setItem(index, 0, it)
        group = QButtonGroup(searchView)
        for i, name in enumerate(colsNames):
            button = QRadioButton()
            group.addButton(button)
            searchView.setCellWidget(index, i + 1, button)
            if name == letter:
                button.setChecked(True)
        index += 1

searchView.show()
sys.exit(app.exec_())

当调整列或行大小时,我注意到一种奇怪的行为:当我按住鼠标按钮调整列或行时,QRadioButtons仍然保持静止,导致一些冲突;然后,当我最终释放鼠标按钮时,每个QRadioButton回到它的位置。有没有办法避免这种情况,即在调整大小过程中使QRadioButtons也移动?
2个回答

3

由于我的先前的解决方案会产生其他问题,所以在本解决方案中,我将展示另一种替代方法:

  • 通过使用Qt :: CheckStateRole的委托实现排除逻辑。
  • 可以使用QProxyStyle进行绘制。
import sys

from PySide2.QtCore import Qt, QEvent
from PySide2.QtWidgets import (
    QApplication,
    QTableWidget,
    QTableWidgetItem,
    QStyledItemDelegate,
    QStyle,
    QStyleOptionViewItem,
    QProxyStyle,
)


class RadioButtonDelegate(QStyledItemDelegate):
    def editorEvent(self, event, model, option, index):
        flags = model.flags(index)
        if (
            not (flags & Qt.ItemIsUserCheckable)
            or not (option.state & QStyle.State_Enabled)
            or not (flags & Qt.ItemIsEnabled)
        ):
            return False
        state = index.data(Qt.CheckStateRole)
        if state is None:
            return False
        widget = option.widget
        style = widget.style() if widget is not None else QApplication.style()
        # make sure that we have the right event type
        if (
            (event.type() == QEvent.MouseButtonRelease)
            or (event.type() == QEvent.MouseButtonDblClick)
            or (event.type() == QEvent.MouseButtonPress)
        ):
            viewOpt = QStyleOptionViewItem(option)
            self.initStyleOption(viewOpt, index)
            checkRect = style.subElementRect(
                QStyle.SE_ItemViewItemCheckIndicator, viewOpt, widget
            )
            me = event
            if me.button() != Qt.LeftButton or not checkRect.contains(me.pos()):
                return False
            if (event.type() == QEvent.MouseButtonPress) or (
                event.type() == QEvent.MouseButtonDblClick
            ):
                return True
        else:
            return False

        if state != Qt.Checked:
            for c in range(model.columnCount()):
                if c not in (0, index.column()):
                    ix = model.index(index.row(), c)
                    model.setData(ix, Qt.Unchecked, Qt.CheckStateRole)
            return model.setData(index, Qt.Checked, Qt.CheckStateRole)
        return False


class RadioStyle(QProxyStyle):
    def drawPrimitive(self, element, option, painter, widget=None):
        if element == QStyle.PE_IndicatorItemViewItemCheck:
            element = QStyle.PE_IndicatorRadioButton
        super().drawPrimitive(element, option, painter, widget)


app = QApplication(sys.argv)

searchView = QTableWidget(0, 4)
style = RadioStyle(searchView.style())
searchView.setStyle(style)

delegate = RadioButtonDelegate(searchView)
searchView.setItemDelegate(delegate)

colsNames = ["A", "B", "C"]
searchView.setHorizontalHeaderLabels(["DIR"] + colsNames)
dirNames = {"A": ["/tmp", "/tmp/dir1"], "B": ["/tmp/dir2"], "C": ["/tmp/dir3"]}

rowCount = sum(len(v) for (name, v) in dirNames.items())
searchView.setRowCount(rowCount)

index = 0

for letter, paths in dirNames.items():
    for path in paths:
        it = QTableWidgetItem(path)
        searchView.setItem(index, 0, it)
        for i, name in enumerate(colsNames):
            it = QTableWidgetItem()
            searchView.setItem(index, i + 1, it)
            it.setCheckState(Qt.Checked if name == letter else Qt.Unchecked)
        index += 1

searchView.show()
sys.exit(app.exec_())

不错的解决方案,我必须学习一下,因为我是初学者。一个问题:在“原始”解决方案中有什么问题吗?调整大小过程中的奇怪行为是一个错误还是类似的东西?(只是为了理解和学习) - MaPo
1
@MaPo 看起来 cell-Widget 只有在鼠标释放时才会收到大小(宽度)的更改通知,虽然这个问题可以解决,但我更倾向于实现一个更优化的解决方案。 - eyllanesc
为了逐步学习这个主题,我应该阅读什么来修改原始示例以避免“延迟”发布通知? - MaPo

0

我曾经遇到过这个问题,并成功地用一个相当简单的解决方案来解决它。

从你的 QTableWidgetQHeadersectionResized 信号创建一个连接到一个自定义方法(在我的情况下是 _refresh_row_size)。

self.table.horizontalHeader().sectionResized.connect(self._refresh_row_size)
self.table.verticalHeader().sectionResized.connect(self._refresh_row_size)

我只想要第一行和第一列被调整大小,因为那是我插入了一个 QCheckbox 的地方。这是我使用的代码:

def _refresh_row_size(self, logicalIndex, oldSize, newSize):

    self.table.rowResized(0, oldSize, newSize)
    self.table.columnResized(0, oldSize, newSize)

    return

为了更好的理解,这是我添加到QTable中的QTableItem

# Create checkbox widget
self.widget = QtWidgets.QWidget()
self.checkbox = QtWidgets.QCheckBox(self.widget)

self.layout = QtWidgets.QHBoxLayout(self.widget)
self.layout.addWidget(self.checkbox)
self.layout.setAlignment(QtCore.Qt.AlignCenter)
self.layout.setContentsMargins(0, 0, 0, 0)

# Add new row to table
row_position = self.table.rowCount()
self.table.insertRow(row_position)

# Create new table item, add item, add widget
item = QtWidgets.QTableWidgetItem()

self.table.setItem(0, 0, item)
self.table.setCellWidget(0, 0, self.widget)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接