当前位置: 首页 > 面试题库 >

PyQt-QTableView中的复选框列

郗丰
2023-03-14
问题内容

我正在从Pandas数据帧动态创建QTableView。我这里有示例代码。

我可以创建带有复选框的表,但无法使复选框反映模型数据,甚至根本无法更改为未选中状态。

我正在跟踪上一个问题的示例代码,并以@raorao答案作为指导。这将显示表格中的框,但所有功能均不起作用。

任何人都可以提出任何更改建议,或者此代码出了什么问题。为什么它不能反映模型,为什么不能改变?

请在此处查看我的完整示例代码。

编辑之一 :在Frodon评论后更新:更正后的字符串转换为带有比较xxx ==’True’的布尔值

class CheckBoxDelegate(QtGui.QStyledItemDelegate):
    """
    A delegate that places a fully functioning QCheckBox in every
    cell of the column to which it's applied
    """
    def __init__(self, parent):
        QtGui.QItemDelegate.__init__(self, parent)

    def createEditor(self, parent, option, index):
        '''
        Important, otherwise an editor is created if the user clicks in this cell.
        ** Need to hook up a signal to the model
        '''
        return None

    def paint(self, painter, option, index):
        '''
        Paint a checkbox without the label.
        '''
        checked = index.model().data(index, QtCore.Qt.DisplayRole) == 'True'
        check_box_style_option = QtGui.QStyleOptionButton()

        if (index.flags() & QtCore.Qt.ItemIsEditable) > 0:
            check_box_style_option.state |= QtGui.QStyle.State_Enabled
        else:
            check_box_style_option.state |= QtGui.QStyle.State_ReadOnly

        if checked:
            check_box_style_option.state |= QtGui.QStyle.State_On
        else:
            check_box_style_option.state |= QtGui.QStyle.State_Off

        check_box_style_option.rect = self.getCheckBoxRect(option)

        # this will not run - hasFlag does not exist
        #if not index.model().hasFlag(index, QtCore.Qt.ItemIsEditable):
            #check_box_style_option.state |= QtGui.QStyle.State_ReadOnly

        check_box_style_option.state |= QtGui.QStyle.State_Enabled

        QtGui.QApplication.style().drawControl(QtGui.QStyle.CE_CheckBox, check_box_style_option, painter)

    def editorEvent(self, event, model, option, index):
        '''
        Change the data in the model and the state of the checkbox
        if the user presses the left mousebutton or presses
        Key_Space or Key_Select and this cell is editable. Otherwise do nothing.
        '''
        print 'Check Box editor Event detected : ' 
        if not (index.flags() & QtCore.Qt.ItemIsEditable) > 0:
            return False

        print 'Check Box edior Event detected : passed first check' 
        # Do not change the checkbox-state
        if event.type() == QtCore.QEvent.MouseButtonRelease or event.type() == QtCore.QEvent.MouseButtonDblClick:
            if event.button() != QtCore.Qt.LeftButton or not self.getCheckBoxRect(option).contains(event.pos()):
                return False
            if event.type() == QtCore.QEvent.MouseButtonDblClick:
                return True
        elif event.type() == QtCore.QEvent.KeyPress:
            if event.key() != QtCore.Qt.Key_Space and event.key() != QtCore.Qt.Key_Select:
                return False
            else:
                return False

        # Change the checkbox-state
        self.setModelData(None, model, index)
        return True

问题答案:

我为您找到了解决方案。诀窍是:

  1. 编写setData模型的方法
  2. 总是在data方法中返回QVariant

这里是。(我必须创建一个名为的类Dataframe,以使示例在没有熊猫的情况下工作。请用您的所有if has_pandas语句替换所有语句):

from PyQt4 import QtCore, QtGui

has_pandas = False
try:
  import pandas as pd
  has_pandas = True
except:
  pass

class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, parent=None, *args):
        super(TableModel, self).__init__()
        self.datatable = None
        self.headerdata = None

    def update(self, dataIn):
        print 'Updating Model'
        self.datatable = dataIn
        print 'Datatable : {0}'.format(self.datatable)
        if has_pandas:
          headers = dataIn.columns.values
        else:
          headers = dataIn.columns
        header_items = [
                    str(field)
                    for field in headers
        ]
        self.headerdata = header_items
        print 'Headers'
        print self.headerdata

    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.datatable.index)

    def columnCount(self, parent=QtCore.QModelIndex()):
        if has_pandas:
          return len(self.datatable.columns.values)
        else:
          return len(self.datatable.columns)

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if role == QtCore.Qt.DisplayRole:
            i = index.row()
            j = index.column()
            return QtCore.QVariant('{0}'.format(self.datatable.iget_value(i, j)))
        else:
            return QtCore.QVariant()

    def setData(self, index, value, role=QtCore.Qt.DisplayRole):
        if index.column() == 4:
            self.datatable.iset_value(index.row(), 4, value)
            return value
        return value

    def headerData(self, col, orientation, role):
        if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
            return '{0}'.format(self.headerdata[col])

    def flags(self, index):
        if index.column() == 4:
            return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled
        else:
            return QtCore.Qt.ItemIsEnabled


class TableView(QtGui.QTableView):
    """
    A simple table to demonstrate the QComboBox delegate.
    """
    def __init__(self, *args, **kwargs):
        QtGui.QTableView.__init__(self, *args, **kwargs)
        self.setItemDelegateForColumn(4, CheckBoxDelegate(self))


class CheckBoxDelegate(QtGui.QStyledItemDelegate):
    """
    A delegate that places a fully functioning QCheckBox in every
    cell of the column to which it's applied
    """
    def __init__(self, parent):
        QtGui.QItemDelegate.__init__(self, parent)

    def createEditor(self, parent, option, index):
        '''
        Important, otherwise an editor is created if the user clicks in this cell.
        ** Need to hook up a signal to the model
        '''
        return None

    def paint(self, painter, option, index):
        '''
        Paint a checkbox without the label.
        '''

        checked = index.data().toBool()
        check_box_style_option = QtGui.QStyleOptionButton()

        if (index.flags() & QtCore.Qt.ItemIsEditable) > 0:
            check_box_style_option.state |= QtGui.QStyle.State_Enabled
        else:
            check_box_style_option.state |= QtGui.QStyle.State_ReadOnly

        if checked:
            check_box_style_option.state |= QtGui.QStyle.State_On
        else:
            check_box_style_option.state |= QtGui.QStyle.State_Off

        check_box_style_option.rect = self.getCheckBoxRect(option)

        # this will not run - hasFlag does not exist
        #if not index.model().hasFlag(index, QtCore.Qt.ItemIsEditable):
            #check_box_style_option.state |= QtGui.QStyle.State_ReadOnly

        check_box_style_option.state |= QtGui.QStyle.State_Enabled

        QtGui.QApplication.style().drawControl(QtGui.QStyle.CE_CheckBox, check_box_style_option, painter)

    def editorEvent(self, event, model, option, index):
        '''
        Change the data in the model and the state of the checkbox
        if the user presses the left mousebutton or presses
        Key_Space or Key_Select and this cell is editable. Otherwise do nothing.
        '''
        print 'Check Box editor Event detected : '
        print event.type()
        if not (index.flags() & QtCore.Qt.ItemIsEditable) > 0:
            return False

        print 'Check Box editor Event detected : passed first check'
        # Do not change the checkbox-state
        if event.type() == QtCore.QEvent.MouseButtonPress:
          return False
        if event.type() == QtCore.QEvent.MouseButtonRelease or event.type() == QtCore.QEvent.MouseButtonDblClick:
            if event.button() != QtCore.Qt.LeftButton or not self.getCheckBoxRect(option).contains(event.pos()):
                return False
            if event.type() == QtCore.QEvent.MouseButtonDblClick:
                return True
        elif event.type() == QtCore.QEvent.KeyPress:
            if event.key() != QtCore.Qt.Key_Space and event.key() != QtCore.Qt.Key_Select:
                return False
        else:
            return False

        # Change the checkbox-state
        self.setModelData(None, model, index)
        return True

    def setModelData (self, editor, model, index):
        '''
        The user wanted to change the old state in the opposite.
        '''
        print 'SetModelData'
        newValue = not index.data().toBool()
        print 'New Value : {0}'.format(newValue)
        model.setData(index, newValue, QtCore.Qt.EditRole)

    def getCheckBoxRect(self, option):
        check_box_style_option = QtGui.QStyleOptionButton()
        check_box_rect = QtGui.QApplication.style().subElementRect(QtGui.QStyle.SE_CheckBoxIndicator, check_box_style_option, None)
        check_box_point = QtCore.QPoint (option.rect.x() +
                            option.rect.width() / 2 -
                            check_box_rect.width() / 2,
                            option.rect.y() +
                            option.rect.height() / 2 -
                            check_box_rect.height() / 2)
        return QtCore.QRect(check_box_point, check_box_rect.size())


###############################################################################################################################
class Dataframe(dict):
  def __init__(self, columns, values):
    if len(values) != len(columns):
      raise Exception("Bad values")
    self.columns = columns
    self.values = values
    self.index = values[0]
    super(Dataframe, self).__init__(dict(zip(columns, values)))
    pass

  def iget_value(self, i, j):
    return(self.values[j][i])

  def iset_value(self, i, j, value):
    self.values[j][i] = value


if __name__=="__main__":
    from sys import argv, exit

    class Widget(QtGui.QWidget):
        """
        A simple test widget to contain and own the model and table.
        """
        def __init__(self, parent=None):
            QtGui.QWidget.__init__(self, parent)

            l=QtGui.QVBoxLayout(self)
            cdf = self.get_data_frame()
            self._tm=TableModel(self)
            self._tm.update(cdf)
            self._tv=TableView(self)
            self._tv.setModel(self._tm)
            for row in range(0, self._tm.rowCount()):
                self._tv.openPersistentEditor(self._tm.index(row, 4))
            self.setGeometry(300, 300, 550, 200)
            l.addWidget(self._tv)

        def get_data_frame(self):
            if has_pandas:
              df = pd.DataFrame({'Name':['a','b','c','d'],
              'First':[2.3,5.4,3.1,7.7], 'Last':[23.4,11.2,65.3,88.8], 'Class':[1,1,2,1], 'Valid':[True, False, True, False]})
            else:
              columns = ['Name', 'First', 'Last', 'Class', 'Valid']
              values = [['a','b','c','d'], [2.3,5.4,3.1,7.7], [23.4,11.2,65.3,88.8], [1,1,2,1], [True, False, True, False]]
              df = Dataframe(columns, values)
            return df

    a=QtGui.QApplication(argv)
    w=Widget()
    w.show()
    w.raise_()
    exit(a.exec_())


 类似资料:
  • 我试图创建一个表单,该表单顶部有一个复选框,当用户选中该复选框时,它会选择其他特定的复选框,但不是所有复选框。我很难通过反复试验或搜索找到答案。我唯一能找到的就是“全选”选项。不是我想要的。 理想情况下,当用户选中“管理包”旁边的复选框时,我想要“Chrome外观组”和“远程启动” 下面是代码和我在这方面的基本尝试,但它不起作用。提前谢谢。 超文本标记语言: Javascript 我不知道这个Ja

  • 我希望在选中表中的所有复选框时选中标题复选框,如果没有选中单个复选框,则应取消选中标题复选框。

  • 问题内容: 我使用以下代码在我的第9列中放置一个复选框 哪里是我的。我需要将单击复选框所在的行添加到列表中。.到底该如何实现? 亲切的问候, 问题答案: 一种方法是: 将表的信号连接到处理程序 在处理程序中测试单击项的 如果选中该项目,则将其添加到列表中 示例代码:

  • 我正在尝试一个Woocommerce票证插件。默认情况下,该插件会显示一个表,其中包含多个票证变体作为产品,并在下面显示一个文本字段作为其数量和一个提交按钮,以将产品添加到购物车中。 我想用单选按钮或复选框替换这个文本字段,这样用户就可以选择一种类型的票证,然后点击提交按钮将其中的一部分添加到购物车中。 我的想法是在foreach循环中创建一个复选框或单选按钮,该复选框或单选按钮的值为quanti

  • 下面是我的代码。 问题是当我选中一个复选框时,包括选中循环视图中的其他复选框。但当我签入适配器时,项目被正确添加,但复选框被复制。 例如:如果我选中了第一项复选框,向下滚动第16项复选框也会被选中。取消选中该复选框也将取消选中第一项。

  • 如果选中复选框D并取消选中其他复选框,我希望选中复选框K和F。同样,如果选中复选框I,则前面的复选框I将被选中,下面的复选框将被取消选中。我的html代码是: 我为此所做的是: 这看起来很乱,还有一个很酷的过程吗???