对于PyQT5小部件,我需要将SQL查询中的数据显示到SQLite数据库,其中列和行被反转/旋转。理想情况下,在QTableView中。 (这个表只有2列,一个用于以前的列名,一个用于它们的值。该表用于显示将在SQL查询中聚合的统计信息,它将只返回一行。所以我想从一行有多列,有两列有多行。)
我提出了一个使用QFormLayout做正确事情的解决方法,但它看起来很丑,看起来非常不优雅。 (参见display_data(self)
方法。)
#!/usr/bin/python3
from PyQt5 import QtSql
from PyQt5.QtWidgets import (QFormLayout, QWidget,
QLabel, QLineEdit, QApplication)
import sys
class InvertedTable(QWidget):
def __init__(self, company):
super().__init__()
self.db_file = "test.db"
self.company = company
self.create_connection()
self.fill_table()
self.init_UI()
self.display_data()
def create_connection(self):
self.db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
self.db.setDatabaseName(self.db_file)
if not self.db.open():
print("Cannot establish a database connection to {}!".format(self.db_file))
return False
def fill_table(self):
self.db.transaction()
q = QtSql.QSqlQuery()
q.exec_("DROP TABLE IF EXISTS Cars;")
q.exec_("""CREATE TABLE Cars (Company TEXT, Model TEXT, Cars TEXT)""")
q.exec_("INSERT INTO Cars VALUES ('Honda', 'Civic', 5)")
q.exec_("INSERT INTO Cars VALUES ('Volkswagen', 'Golf', 3)")
self.db.commit()
def init_UI(self):
self.resize(300,100)
self.layout = QFormLayout()
self.setLayout(self.layout)
def display_data(self):
query = "select * from cars where company = '{}'".format(self.company)
q = QtSql.QSqlQuery()
q.exec_(query)
self.check_error(q)
record = q.record()
columns = record.count()
q.next()
for i in range(columns):
column_name = record.field(i).name()
col_field = QLabel(column_name, self)
value = q.value(i)
value_field = QLineEdit(self)
value_field.setText(value)
self.layout.addRow(col_field, value_field)
def closeEvent(self, e):
if (self.db.open()):
self.db.close()
def check_error(self, q):
lasterr = q.lastError()
if lasterr.isValid():
print(lasterr.text())
self.db.close()
exit(1)
def main():
app = QApplication(sys.argv)
ex = InvertedTable("Honda")
ex.show()
result = app.exec_()
sys.exit(result)
if __name__ == '__main__':
main()
使用QTableView实现此目的的正确方法是什么?
答案 0 :(得分:1)
使用QTableView
的正确方法是拥有QTableModel
。
幸运的是,有一个QSqlTableModel允许您针对SQL表构建表模型。
Funt回答了一个类似的问题,指出QIdentityProxyModel可以使用"在此基础上#34;通过重新定义mapToSource
和mapFromSource
方法来更改数据模型的表示形式。
还有一些方法可以直接从SQL命令转置SQL请求的结果。请参阅here。
另外值得一读:Model-View Programming with Qt。它是C ++版本,但PyQt遵循相同的原则(并且类具有相同的名称)。
希望有所帮助。
答案 1 :(得分:1)
经过一些搜索并阅读@PlikPlok留下的有用指示后,我找到了一个解决方案here:
显然,任何开箱即用的Qt类都不提供此功能,因此您必须将QAbstractProxyModel和QSqlRelationalDelegate子类化,然后在表上使用它们:
#!/usr/bin/python3
import sys
from PyQt5 import QtSql
from PyQt5.QtWidgets import (QWidget, QApplication,
QGridLayout, QTableView)
from PyQt5.Qt import (QModelIndex, QAbstractProxyModel, QSqlRelationalDelegate)
from PyQt5.QtCore import Qt
class FlippedProxyModel(QAbstractProxyModel):
def __init__(self, parent=None):
super().__init__(parent)
def mapFromSource(self, index):
return self.createIndex(index.column(), index.row())
def mapToSource(self, index):
return self.sourceModel().index(index.column(), index.row(), QModelIndex())
def columnCount(self, parent):
return self.sourceModel().rowCount(QModelIndex())
def rowCount(self, parent):
return self.sourceModel().columnCount(QModelIndex())
def index(self, row, column, parent):
return self.createIndex(row, column)
def parent(self, index):
return QModelIndex()
def data(self, index, role):
return self.sourceModel().data(self.mapToSource(index), role)
def headerData(self, section, orientation, role):
if orientation == Qt.Horizontal:
return self.sourceModel().headerData(section, Qt.Vertical, role)
if orientation == Qt.Vertical:
return self.sourceModel().headerData(section, Qt.Horizontal, role)
class FlippedProxyDelegate(QSqlRelationalDelegate):
def createEditor(self, parent, option, index):
proxy = index.model()
base_index = proxy.mapToSource(index)
return super().createEditor(parent, option, base_index)
def setEditorData(self, editor, index):
proxy = index.model()
base_index = proxy.mapToSource(index)
return super().setEditorData(editor, base_index)
def setModelData(self, editor, model, index):
base_model = model.sourceModel()
base_index = model.mapToSource(index)
return super().setModelData(editor, base_model, base_index)
class InvertedTable(QWidget):
def __init__(self, company):
super().__init__()
self.db_file = "test.db"
self.company = company
self.create_connection()
self.fill_table()
self.create_model()
self.init_UI()
def create_connection(self):
self.db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
self.db.setDatabaseName(self.db_file)
if not self.db.open():
print("Cannot establish a database connection to {}!".format(self.db_file))
return False
def fill_table(self):
self.db.transaction()
q = QtSql.QSqlQuery()
q.exec_("DROP TABLE IF EXISTS Cars;")
q.exec_("""CREATE TABLE Cars (Company TEXT, Model TEXT, Cars TEXT)""")
q.exec_("INSERT INTO Cars VALUES ('Honda', 'Civic', 5)")
q.exec_("INSERT INTO Cars VALUES ('Volkswagen', 'Golf', 3)")
self.db.commit()
def create_model(self):
self.model = QtSql.QSqlTableModel()
q = QtSql.QSqlQuery()
query = """SELECT * from cars where company = 'Honda'
"""
q.exec_(query)
self.model.setQuery(q)
self.proxy = FlippedProxyModel() # use flipped proxy model
self.proxy.setSourceModel(self.model)
def init_UI(self):
self.grid = QGridLayout()
self.setLayout(self.grid)
self.table = QTableView()
self.table.setModel(self.proxy)
self.table.setItemDelegate(FlippedProxyDelegate(self.table)) # use flipped proxy delegate
self.table.horizontalHeader().hide()
self.grid.addWidget(self.table, 0, 0)
def closeEvent(self, e):
if (self.db.open()):
self.db.close()
def check_error(self, q):
lasterr = q.lastError()
if lasterr.isValid():
print(lasterr.text())
self.db.close()
exit(1)
def main():
app = QApplication(sys.argv)
ex = InvertedTable("Honda")
ex.show()
result = app.exec_()
sys.exit(result)
if __name__ == '__main__':
main()