在运行时更新 QStackedWidget 内容
Posted
技术标签:
【中文标题】在运行时更新 QStackedWidget 内容【英文标题】:Updating QStackedWidget content at Runtime 【发布时间】:2018-04-03 15:01:41 【问题描述】:我正在 PyQT5 中创建一个 GUI,它应该提供来自多个相关 SQLite 表的数据的多个视图。我已经实现了这些视图以通过 QStackedWidget 显示。
现在,其中一些视图用于概览目的,其他视图用于对概览中显示的数据子集进行更详细的视图。我想通过右键单击从概览中访问详细视图。
我在下面提供了一个带有汽车的最小示例。 (抱歉,它有点长,但需要提供一个完整的工作示例。我已尽可能减少它。)目标是显示 DetailledView 以及在概览中选择的公司的汽车。
这已经提供了从概览到右键单击上的 DetailedView 的访问权限,但公司信息并未传递。因此,即使从“VW”访问 DetailledView,self.mycompany 也会更新,但 car_widget 不会,因此 DetailledView 会显示有关“Honda”汽车的信息(默认)。
有没有办法通过合适的公司更新 car_widgit?还是我需要在运行时创建 DetailledView? (这甚至有效吗?但无论如何我宁愿不这样做,因为它可能使 Stack 的索引不可靠......)
如何根据在另一个视图中选择的内容更新 QStackedWidget 中一个视图的 QTableModel?
代码如下:
#!/usr/bin/python3
from PyQt5 import QtSql
from PyQt5.QtWidgets import (QMainWindow, QWidget, QApplication, QGridLayout,
QStackedWidget, QTableView, QMenu)
from PyQt5.QtCore import Qt, QModelIndex
import sys
class MainGUI(QMainWindow):
def __init__(self):
super().__init__()
self.mycompany = "Honda"
self.create_connection()
self.fill_tables()
self.init_UI()
def create_connection(self):
self.db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
self.db.setDatabaseName("test.db")
if not self.db.open():
print("Cannot establish a database connection to !".format(self.db_file))
return False
def fill_tables(self):
self.db.transaction()
q = QtSql.QSqlQuery()
q.exec_("DROP TABLE IF EXISTS Manufacturers;")
q.exec_("CREATE TABLE Manufacturers (CompanyId INT PRIMARY KEY, Name TEXT, Country TEXT);")
q.exec_("INSERT INTO Manufacturers VALUES (1, 'VW', 'Germany');")
q.exec_("INSERT INTO Manufacturers VALUES (2, 'Honda' , 'Japan');")
q.exec_("DROP TABLE IF EXISTS Cars;")
q.exec_("CREATE TABLE Cars (Model TEXT, Year INT, Company INT);")
q.exec_("INSERT INTO Cars VALUES ('Civic', 2009, 'Honda');")
q.exec_("INSERT INTO Cars VALUES ('Golf', 2013, 'VW');")
q.exec_("INSERT INTO Cars VALUES ('Polo', 1999, 'VW');")
self.db.commit()
def init_UI(self):
self.central_widget = QWidget()
self.setCentralWidget(self.central_widget)
self.grid = QGridLayout()
self.central_widget.setLayout(self.grid)
self.setLayout(self.grid)
self.make_stack()
self.show()
def make_stack(self):
self.Stack = QStackedWidget(self)
company_view = QWidget()
layout = QGridLayout()
company_view.setLayout(layout)
self.company_widget = Overview()
layout.addWidget(self.company_widget, 0, 0)
self.company_widget.table.customContextMenuRequested.connect(self.open_menu)
self.Stack.addWidget(company_view)
car_view = QWidget()
layout2 = QGridLayout()
car_view.setLayout(layout2)
car_widget = DetailedView(self.mycompany)
layout2.addWidget(car_widget, 0, 0)
self.Stack.addWidget(car_view)
self.grid.addWidget(self.Stack, 0,1)
def open_menu(self, pos):
menu = QMenu()
show_act = menu.addAction("Show cars")
action = menu.exec_(self.company_widget.table.mapToGlobal(pos))
if action == show_act:
row = self.company_widget.table.indexAt(pos).row()
myindex = self.company_widget.model.index(row, 1, QModelIndex())
company = self.company_widget.model.data(myindex)
self.mycompany = company
self.Stack.setCurrentIndex(1)
class MyTable(QWidget):
def __init__(self):
super().__init__()
self.create_connection()
self.create_model()
self.init_UI()
def create_connection(self):
self.db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
self.db.setDatabaseName("test.db")
if not self.db.open():
print("Cannot establish a database connection to !".format(self.db_file))
return False
def create_model(self):
self.model = None
def init_UI(self):
self.grid = QGridLayout()
self.setLayout(self.grid)
self.table = QTableView()
self.table.setModel(self.model)
self.table.setContextMenuPolicy(Qt.CustomContextMenu)
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)
class Overview(MyTable):
def __init__(self):
super().__init__()
def create_model(self):
self.model = QtSql.QSqlTableModel()
q = QtSql.QSqlQuery()
query = "SELECT * from Manufacturers"
q.exec_(query)
self.model.setQuery(q)
class DetailedView(MyTable):
def __init__(self, company):
self.company = company
super().__init__()
def create_model(self):
self.model = QtSql.QSqlTableModel()
q = QtSql.QSqlQuery()
query = "SELECT * from cars where company = ''".format(self.company)
q.exec_(query)
self.model.setQuery(q)
def main():
app = QApplication(sys.argv)
ex = MainGUI()
ex.show()
result = app.exec_()
sys.exit(result)
if __name__ == '__main__':
main()
【问题讨论】:
您从未真正重置过company
属性。还是您认为mycompany
是某种指针?字符串在 python 中不是这样工作的。但无论如何,你永远不会在模型上重新调用select()
,所以显然什么都不会改变。我建议您为每个视图提供一个setFilter()
方法,并为选择语句传入必要的参数(并且请使用占位符,而不是字符串格式)。 PS:不需要为每个表创建单独的数据库连接。一个连接就足够了(除非您使用多线程)。
@ekhumoro 我知道公司不会重置,我只是不知道如何重置。
【参考方案1】:
继承的目标之一是该类实现子级可以执行的常见任务,而在您的情况下,您不会观察这些任务,例如模型的创建必须在父级中完成,因为所有孩子们会做的。
另一方面,使用 QSqlTableModel 的目的不是使用QSqlQuery
,而是使用更友好的请求,例如setTable()
、select()
和setFilter()
,但这只是浪费,因为您可以使用QSqlQueryModel
.
另一方面,我看到您假设 MainGui
的 self.mycompany
与 DetailedView
的 self.company
相同,因为您在创建 DetailView 对象时所经历的事情,以及事实是它们是不一样的,在创建对象时只复制了那个时刻的值,所以如果你改变MainGui
的self.company
,它不会改变DetailView
的self.company
。
重组你的项目你会得到以下结果:
#!/usr/bin/python3
from PyQt5.QtSql import QSqlDatabase, QSqlQuery, QSqlTableModel
from PyQt5.QtWidgets import (QMainWindow, QWidget, QApplication, QVBoxLayout,
QStackedWidget, QTableView, QMenu)
from PyQt5.QtCore import Qt, QModelIndex, pyqtSignal
import sys
DB_PATH = "test.db"
def create_connection():
db = QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName(DB_PATH)
if not db.open():
print("Cannot establish a database connection to !".format(DB_PATH))
return False
return True
def fill_tables():
q = QSqlQuery()
q.exec_("DROP TABLE IF EXISTS Manufacturers;")
q.exec_("CREATE TABLE Manufacturers (CompanyId INT PRIMARY KEY, Name TEXT, Country TEXT);")
q.exec_("INSERT INTO Manufacturers VALUES (1, 'VW', 'Germany');")
q.exec_("INSERT INTO Manufacturers VALUES (2, 'Honda' , 'Japan');")
q.exec_("DROP TABLE IF EXISTS Cars;")
q.exec_("CREATE TABLE Cars (Model TEXT, Year INT, Company INT);")
q.exec_("INSERT INTO Cars VALUES ('Civic', 2009, 'Honda');")
q.exec_("INSERT INTO Cars VALUES ('Golf', 2013, 'VW');")
q.exec_("INSERT INTO Cars VALUES ('Polo', 1999, 'VW');")
class MainGUI(QMainWindow):
def __init__(self):
super().__init__()
self.init_UI()
def init_UI(self):
self.central_widget = QWidget()
self.setCentralWidget(self.central_widget)
self.lay = QVBoxLayout(self.central_widget)
self.make_stack()
def make_stack(self):
self.stack = QStackedWidget()
self.lay.addWidget(self.stack)
self.company_widget = Overview()
self.car_widget = DetailedView()
self.stack.addWidget(self.company_widget)
self.stack.addWidget(self.car_widget)
self.company_widget.changedCompany.connect(self.changedCompany)
self.car_widget.backSignal.connect(lambda: self.stack.setCurrentIndex(0))
def changedCompany(self, company):
self.car_widget.filter(company)
self.stack.setCurrentIndex(1)
class SQLTable(QTableView):
def __init__(self, table):
super().__init__()
self.init_UI()
self.create_model(table)
def create_model(self, table):
self.model.setTable(table)
self.model.select()
def init_UI(self):
self.model = QSqlTableModel()
self.setModel(self.model)
self.setContextMenuPolicy(Qt.CustomContextMenu)
class Overview(SQLTable):
changedCompany = pyqtSignal(str)
def __init__(self):
SQLTable.__init__(self, "Manufacturers")
self.customContextMenuRequested.connect(self.open_menu)
def open_menu(self, pos):
menu = QMenu()
show_act = menu.addAction("Show cars")
action = menu.exec_(self.mapToGlobal(pos))
if action == show_act:
row = self.indexAt(pos).row()
ix = myindex = self.model.index(row, 1)
company = self.model.data(ix)
self.changedCompany.emit(company)
class DetailedView(SQLTable):
backSignal = pyqtSignal()
def __init__(self):
SQLTable.__init__(self, "cars")
self.customContextMenuRequested.connect(self.open_menu)
def open_menu(self, pos):
menu = QMenu()
back_act = menu.addAction("Show Manufacturers")
action = menu.exec_(self.mapToGlobal(pos))
if action == back_act:
self.backSignal.emit()
def filter(self, company):
self.model.setFilter("company=''".format(company))
def main():
app = QApplication(sys.argv)
if not create_connection():
sys.exit(-1)
fill_tables()
ex = MainGUI()
ex.show()
result = app.exec_()
sys.exit(result)
if __name__ == '__main__':
main()
【讨论】:
谢谢,你是我的英雄!这教会了我很多东西!以上是关于在运行时更新 QStackedWidget 内容的主要内容,如果未能解决你的问题,请参考以下文章
PyQt:如何在 QStackedWidget 中切换小部件