如何使用QCombobox选择更新QTableView单元?

时间:2018-01-04 23:22:43

标签: python-3.x pyqt delegates qtableview qcombobox

(我是PyQt的初学者。)

我想在一些QTableView行中的特定单元格中添加委托QComboBox委托。我发现了几篇关于如何添加委托的帖子,但是没有一篇文章提供了使用QComboBox选择更新单元格的示例。

这是我到目前为止所做的:

main.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Dialog</class>
 <widget class="QDialog" name="Dialog">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>350</width>
    <height>239</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Dialog</string>
  </property>
  <widget class="QWidget" name="formLayoutWidget">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>341</width>
     <height>231</height>
    </rect>
   </property>
   <layout class="QFormLayout" name="formLayout">
    <item row="0" column="1">
     <widget class="QPushButton" name="btnPopulate">
      <property name="text">
       <string>Populate Table</string>
      </property>
     </widget>
    </item>
    <item row="1" column="1">
     <widget class="QTableView" name="tableView"/>
    </item>
   </layout>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

test.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys, os
from PyQt5 import uic, QtWidgets
from PyQt5.QtGui import QStandardItemModel
from PyQt5.QtWidgets import QDialog, QComboBox, QApplication

class GUI(QDialog):

    def __init__(self):
        super(GUI, self).__init__()
        dirname = os.path.dirname(os.path.abspath(__file__))
        uic.loadUi(os.path.join(dirname,'main.ui'), self)
        # button
        self.btnPopulate.clicked.connect(self.populate)
        # table model
        self.header = ['col1', 'col2', 'col3']
        self.QSModel = QStandardItemModel()
        self.QSModel.setColumnCount(3)
        self.QSModel.setHorizontalHeaderLabels(self.header)
        self.tableView.setModel(self.QSModel)
        # combobox delegate
        self.cbDelegate = QComboBox()
        self.cbDelegate.addItems(['choice1', 'choice2'])

    def populate(self):
        row = self.QSModel.rowCount()
        for x in range(0, 7):
            self.QSModel.insertRow(row)
            self.QSModel.setData(self.QSModel.index(row, 0), 'data')
            self.QSModel.item(row, 0).setEditable(True)
            self.QSModel.setData(self.QSModel.index(row, 1), 'data')
            self.QSModel.item(row, 1).setEditable(True)
            # add combobox delegate to even rows
            if x % 2 == 0:
                print('Delegate added.')
                index = self.tableView.model().index(row, 1)
                self.tableView.setIndexWidget(index, self.cbDelegate)
            self.QSModel.setData(self.QSModel.index(row, 2), 'data')
            self.QSModel.item(row, 1).setEditable(True)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = GUI()
    window.show()
    sys.exit(app.exec_())

但是,当我单击填充表按钮时,只显示第一行中的第二个单元格,其中包含QComboBox。

  1. 如何更改代码以在第0行,第2行,第4行和第6行或任意行中显示QComboBox。 (是否需要显示QComboBox取决于到达行中第一个单元格的值。)

  2. 使用QComboBox选择替换单元格内容需要使用哪种方法?

1 个答案:

答案 0 :(得分:1)

  

如何更改代码以在第0行显示QComboBox,   2,4和6或任何任意行。 (无论是否   需要显示QComboBox取决于第一个单元格的值   在达到行。)

问题所以你只能看到QComboBox是因为你只创建了一个QComboBox来解决它,你必须为你想要建立它们的每个单元创建一个QComboBox。

  

我需要用什么样的方法来替换细胞内容   QComboBox选择?

你必须将相应的信号连接到模型的setData()方法,我们可以使用QModelIndex,但这很危险,因为删除,移动或插入项目时可以更改它,适合使用QPersistentModelIndex,在这种情况下,您必须使用信号currentIndexChanged

在此示例中,我将使用QModelIndex

def populate(self):
    row = self.QSModel.rowCount()
    for x in range(7):
        self.QSModel.insertRow(row)
        self.QSModel.setData(self.QSModel.index(row, 0), 'data')
        self.QSModel.item(row, 0).setEditable(True)
        self.QSModel.setData(self.QSModel.index(row, 1), 'data')
        self.QSModel.item(row, 1).setEditable(True)
        # add combobox delegate to even rows
        if x % 2 == 0:
            index = self.tableView.model().index(row, 1)
            cbDelegate = QComboBox()
            pix = QPersistentModelIndex(index)
            cbDelegate.currentIndexChanged[str].connect(lambda txt, pix=pix:self.tableView.model().setData(QModelIndex(pix), txt))
            cbDelegate.addItems(['choice1', 'choice2'])
            self.tableView.setIndexWidget(index, cbDelegate)
        self.QSModel.setData(self.QSModel.index(row, 2), 'data')
        self.QSModel.item(row, 1).setEditable(True)

在此示例中,我将使用QStandartItem

def populate(self):
    row = self.QSModel.rowCount()
    for x in range(7):
        self.QSModel.insertRow(row)
        self.QSModel.setData(self.QSModel.index(row, 0), 'data')
        self.QSModel.item(row, 0).setEditable(True)
        self.QSModel.setData(self.QSModel.index(row, 1), 'data')
        self.QSModel.item(row, 1).setEditable(True)
        if x % 2 == 0:
            item = self.tableView.model().item(row, 1)
            cbDelegate = QComboBox()
            cbDelegate.currentIndexChanged[str].connect(item.setText)
            cbDelegate.addItems(['choice1', 'choice2'])
            self.tableView.setIndexWidget(item.index(), cbDelegate)
        self.QSModel.setData(self.QSModel.index(row, 2), 'data')
        self.QSModel.item(row, 1).setEditable(True)

执行类似操作的另一种方法是使用委托,因为我们创建了一个继承自QStyledItemDelegate的类:

class ComboBoxDelegate(QStyledItemDelegate):
    def paint(self, painter, option, index):
        if index.row()%2 == 0:
            opt = QStyleOptionComboBox()
            opt.rect = option.rect 
            opt.currentText = index.data()
            QApplication.style().drawComplexControl(QStyle.CC_ComboBox, opt, painter)
            QApplication.style().drawControl(QStyle.CE_ComboBoxLabel, opt, painter)
        else:
            QStyledItemDelegate.paint(self, painter, option, index)

    def createEditor(self, parent, option, index):
        if index.row()%2 == 0:
            combobox = QComboBox(parent)
            options = ['choice1', 'choice2']
            if index.data() not in options:
                combobox.addItem(index.data())
            combobox.addItems(options)
            return combobox
        return QStyledItemDelegate.createEditor(self, parent, option, index)

    def setEditorData(self, editor, index):
        if isinstance(editor, QComboBox):
            text = index.data()
            ix = editor.findText(text)
            if ix > 0:
                editor.setCurrentIndex(ix)
            else:
                index.model().setData(index, editor.currentText())
        else:
            QStyledItemDelegate.setEditorData(self, editor, index)

    def setModelData(self, editor, model, index):
        if isinstance(editor, QComboBox):
            model.setData(index, editor.currentText())
        else:
            QStyledItemDelegate.setModelData(self, editor, model, index)

class GUI(QDialog):
    def __init__(self):
        super(GUI, self).__init__()
        dirname = os.path.dirname(os.path.abspath(__file__))
        uic.loadUi(os.path.join(dirname,'main.ui'), self)
        self.btnPopulate.clicked.connect(self.populate)
        self.header = ['col1', 'col2', 'col3']
        self.QSModel = QStandardItemModel()
        self.QSModel.setColumnCount(3)
        self.QSModel.setHorizontalHeaderLabels(self.header)
        self.tableView.setModel(self.QSModel)
        self.tableView.setItemDelegateForColumn(1, ComboBoxDelegate(self.tableView))
        self.populate()

    def populate(self):
        row = self.QSModel.rowCount()
        for x in range(7):
            for i, value in enumerate(['data1', 'data2', 'data3']):
                self.QSModel.setItem(row+x, i, QStandardItem(value))
                self.QSModel.item(row+x, i).setEditable(True)