PyQt - QTableview 中的 QCombobox
PyQt - QCombobox in QTableview
我正在使用 QSqlTableModel 在 QTableView 中显示来自 SQLite 数据库的数据。让用户编辑此数据效果很好。但是,对于某些列,我想使用 QComboboxes 而不是自由文本单元格,以限制可能答案的列表。
我已经找到 this SO answer 并且正在尝试在我的 model/view 设置上实施它,但是我 运行 遇到了问题(所以这是后续行动)。
这是一个完整的小例子:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from PyQt5 import QtSql
from PyQt5.QtWidgets import (QWidget, QTableView, QApplication, QHBoxLayout,
QItemDelegate, QComboBox)
from PyQt5.QtCore import pyqtSlot
import sys
class ComboDelegate(QItemDelegate):
"""
A delegate that places a fully functioning QComboBox in every
cell of the column to which it's applied
source: https://gist.github.com/Riateche/5984815
"""
def __init__(self, parent, items):
self.items = items
QItemDelegate.__init__(self, parent)
def createEditor(self, parent, option, index):
combo = QComboBox(parent)
li = []
for item in self.items:
li.append(item)
combo.addItems(li)
combo.currentIndexChanged.connect(self.currentIndexChanged)
return combo
def setEditorData(self, editor, index):
editor.blockSignals(True)
# editor.setCurrentIndex(int(index.model().data(index))) #from original code
editor.setCurrentIndex(index.row()) # replacement
editor.blockSignals(False)
def setModelData(self, editor, model, index):
model.setData(index, editor.currentIndex())
@pyqtSlot()
def currentIndexChanged(self):
self.commitData.emit(self.sender())
class Example(QWidget):
def __init__(self):
super().__init__()
self.resize(400, 150)
self.createConnection()
self.fillTable() # comment out to skip re-creating the SQL table
self.createModel()
self.initUI()
def createConnection(self):
self.db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
self.db.setDatabaseName("test.db")
if not self.db.open():
print("Cannot establish a database connection")
return False
def fillTable(self):
self.db.transaction()
q = QtSql.QSqlQuery()
q.exec_("DROP TABLE IF EXISTS Cars;")
q.exec_("CREATE TABLE Cars (Company TEXT, Model TEXT, Year NUMBER);")
q.exec_("INSERT INTO Cars VALUES ('Honda', 'Civic', 2009);")
q.exec_("INSERT INTO Cars VALUES ('VW', 'Golf', 2013);")
q.exec_("INSERT INTO Cars VALUES ('VW', 'Polo', 1999);")
self.db.commit()
def createModel(self):
self.model = QtSql.QSqlTableModel()
self.model.setTable("Cars")
self.model.select()
def initUI(self):
layout = QHBoxLayout()
self.setLayout(layout)
view = QTableView()
layout.addWidget(view)
view.setModel(self.model)
view.setItemDelegateForColumn(0, ComboDelegate(self, ["VW", "Honda"]))
for row in range(0, self.model.rowCount()):
view.openPersistentEditor(self.model.index(row, 0))
def closeEvent(self, e):
for row in range(self.model.rowCount()):
print("row {}: company = {}".format(row, self.model.data(self.model.index(row, 0))))
if (self.db.open()):
self.db.close()
def main():
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
在这种情况下,我想在 "Company" 列上使用 QCombobox。它应该一直显示,所以我调用了 openPersistentEditor。
问题 1:默认值 我希望这会在未编辑时显示未编辑字段的内容(即模型中列出的公司),而是它显然显示了组合框选择的第 i 个元素。
如何让每个组合框默认显示该字段的模型实际内容?
问题 2:编辑 当您注释掉 "self.fill_table()" 时,您可以检查编辑是否到达 SQL 数据库。我希望选择下拉列表中的任何字段将替换原始值。但是 (a) 我必须对每个选择进行两次(第一次,单元格中显示的值保持不变),并且 (b) 数据奇怪地出现在模型中(将第一列更改为 'VW', 'Honda'、'Honda' 结果为(模型中的“1”、'VW'、“1”)。我认为这是因为代码在委托中使用了 editor.currentIndex() setModelData,但我还没有找到一种方法来代替使用编辑器的内容。如何让代码将用户的选择正确地报告回模型?(以及如何使它起作用第一次点击,而不是需要 2 次点击?)
非常感谢任何帮助。 (我 阅读了 documentation on QAbstractItemDelegate,但我觉得它不是特别有用。)
在书的帮助下找到了解决方案Rapid GUI Programming with Python and Qt:
createEditor
和 setEditorData
没有像我预期的那样工作(我被误导了,因为示例代码看起来像是在使用文本内容,但实际上是在处理索引号)。相反,它们应该如下所示:
def setEditorData(self, editor, index):
editor.blockSignals(True)
text = index.model().data(index, Qt.DisplayRole)
try:
i = self.items.index(text)
except ValueError:
i = 0
editor.setCurrentIndex(i)
editor.blockSignals(False)
def setModelData(self, editor, model, index):
model.setData(index, editor.currentText())
我希望这对在线的人有所帮助。
我正在使用 QSqlTableModel 在 QTableView 中显示来自 SQLite 数据库的数据。让用户编辑此数据效果很好。但是,对于某些列,我想使用 QComboboxes 而不是自由文本单元格,以限制可能答案的列表。
我已经找到 this SO answer 并且正在尝试在我的 model/view 设置上实施它,但是我 运行 遇到了问题(所以这是后续行动)。
这是一个完整的小例子:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from PyQt5 import QtSql
from PyQt5.QtWidgets import (QWidget, QTableView, QApplication, QHBoxLayout,
QItemDelegate, QComboBox)
from PyQt5.QtCore import pyqtSlot
import sys
class ComboDelegate(QItemDelegate):
"""
A delegate that places a fully functioning QComboBox in every
cell of the column to which it's applied
source: https://gist.github.com/Riateche/5984815
"""
def __init__(self, parent, items):
self.items = items
QItemDelegate.__init__(self, parent)
def createEditor(self, parent, option, index):
combo = QComboBox(parent)
li = []
for item in self.items:
li.append(item)
combo.addItems(li)
combo.currentIndexChanged.connect(self.currentIndexChanged)
return combo
def setEditorData(self, editor, index):
editor.blockSignals(True)
# editor.setCurrentIndex(int(index.model().data(index))) #from original code
editor.setCurrentIndex(index.row()) # replacement
editor.blockSignals(False)
def setModelData(self, editor, model, index):
model.setData(index, editor.currentIndex())
@pyqtSlot()
def currentIndexChanged(self):
self.commitData.emit(self.sender())
class Example(QWidget):
def __init__(self):
super().__init__()
self.resize(400, 150)
self.createConnection()
self.fillTable() # comment out to skip re-creating the SQL table
self.createModel()
self.initUI()
def createConnection(self):
self.db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
self.db.setDatabaseName("test.db")
if not self.db.open():
print("Cannot establish a database connection")
return False
def fillTable(self):
self.db.transaction()
q = QtSql.QSqlQuery()
q.exec_("DROP TABLE IF EXISTS Cars;")
q.exec_("CREATE TABLE Cars (Company TEXT, Model TEXT, Year NUMBER);")
q.exec_("INSERT INTO Cars VALUES ('Honda', 'Civic', 2009);")
q.exec_("INSERT INTO Cars VALUES ('VW', 'Golf', 2013);")
q.exec_("INSERT INTO Cars VALUES ('VW', 'Polo', 1999);")
self.db.commit()
def createModel(self):
self.model = QtSql.QSqlTableModel()
self.model.setTable("Cars")
self.model.select()
def initUI(self):
layout = QHBoxLayout()
self.setLayout(layout)
view = QTableView()
layout.addWidget(view)
view.setModel(self.model)
view.setItemDelegateForColumn(0, ComboDelegate(self, ["VW", "Honda"]))
for row in range(0, self.model.rowCount()):
view.openPersistentEditor(self.model.index(row, 0))
def closeEvent(self, e):
for row in range(self.model.rowCount()):
print("row {}: company = {}".format(row, self.model.data(self.model.index(row, 0))))
if (self.db.open()):
self.db.close()
def main():
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
在这种情况下,我想在 "Company" 列上使用 QCombobox。它应该一直显示,所以我调用了 openPersistentEditor。
问题 1:默认值 我希望这会在未编辑时显示未编辑字段的内容(即模型中列出的公司),而是它显然显示了组合框选择的第 i 个元素。 如何让每个组合框默认显示该字段的模型实际内容?
问题 2:编辑 当您注释掉 "self.fill_table()" 时,您可以检查编辑是否到达 SQL 数据库。我希望选择下拉列表中的任何字段将替换原始值。但是 (a) 我必须对每个选择进行两次(第一次,单元格中显示的值保持不变),并且 (b) 数据奇怪地出现在模型中(将第一列更改为 'VW', 'Honda'、'Honda' 结果为(模型中的“1”、'VW'、“1”)。我认为这是因为代码在委托中使用了 editor.currentIndex() setModelData,但我还没有找到一种方法来代替使用编辑器的内容。如何让代码将用户的选择正确地报告回模型?(以及如何使它起作用第一次点击,而不是需要 2 次点击?)
非常感谢任何帮助。 (我 阅读了 documentation on QAbstractItemDelegate,但我觉得它不是特别有用。)
在书的帮助下找到了解决方案Rapid GUI Programming with Python and Qt:
createEditor
和 setEditorData
没有像我预期的那样工作(我被误导了,因为示例代码看起来像是在使用文本内容,但实际上是在处理索引号)。相反,它们应该如下所示:
def setEditorData(self, editor, index):
editor.blockSignals(True)
text = index.model().data(index, Qt.DisplayRole)
try:
i = self.items.index(text)
except ValueError:
i = 0
editor.setCurrentIndex(i)
editor.blockSignals(False)
def setModelData(self, editor, model, index):
model.setData(index, editor.currentText())
我希望这对在线的人有所帮助。