Обновление содержимого QStackedWidget во время выполнения

Я создаю графический интерфейс в PyQT5, который должен обеспечивать несколько представлений данных из нескольких связанных таблиц SQLite. Я реализовал эти представления для отображения через QStackedWidget.

Теперь некоторые из этих представлений предназначены для обзора, другие — для более подробного просмотра подмножества данных, отображаемых в обзорах. Я хочу получить доступ к подробным представлениям из обзоров, щелкнув правой кнопкой мыши.

Я включил минимальный пример с автомобилями ниже. (Извините, это немного длинно, но это было необходимо для предоставления полного рабочего примера. Я сократил его настолько, насколько мог.) Цель состоит в том, чтобы показать DetailledView с автомобилями компании, выбранными в обзоре.

Это уже обеспечивает доступ из Обзора к Подробному представлению при щелчке правой кнопкой мыши, но информация о компании не передается. Таким образом, даже при доступе к DetailledView из «VW» self.mycompany обновляется, а car_widget — нет, поэтому DetailledView показывает информацию об автомобилях «Honda» (по умолчанию).

Есть ли способ обновить car_widgit с помощью правильной компании? Или мне нужно создать DetailledView во время выполнения? (Это вообще работает? Но в любом случае я бы предпочел этого не делать, так как это может сделать индекс стека ненадежным...)

Как я могу обновить QTableModel одного представления в QStackedWidget в соответствии с тем, что было выбрано в другом представлении?

Вот код:

#!/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()      

person CodingCat    schedule 03.04.2018    source источник
comment
На самом деле вы никогда не сбрасываете атрибут company. Или вы думали, что mycompany это какой-то указатель? Строки в питоне так не работают. Но в любом случае вы никогда не будете повторно вызывать select() на модели, так что явно ничего не изменится. Я предлагаю вам предоставить метод setFilter() для каждого представления и передать необходимые параметры для оператора select (и, пожалуйста, используйте заполнители, а не форматирование строк). PS: нет необходимости создавать отдельное подключение к базе данных для каждой таблицы. Достаточно одного подключения (если вы не используете многопоточность).   -  person ekhumoro    schedule 03.04.2018
comment
@ekhumoro Я знал, что эта компания не перезагружается, просто не знал, как это сделать.   -  person CodingCat    schedule 04.04.2018


Ответы (1)


Одной из целей наследования является то, что класс реализует общие задачи, которые могут выполнять дети, а в вашем случае вы не наблюдаете эти задачи, например создание модели должно быть сделано в отце, так как все дети будут сделай это.

С другой стороны, цель использования QSqlTableModel состоит не в том, чтобы использовать QSqlQuery, а в более дружественных запросах, таких как setTable(), select() и setFilter(), но это просто пустая трата времени, потому что вы можете использовать QSqlQueryModel.

С другой стороны, я вижу, что вы предполагаете, что self.mycompany из MainGui совпадает с self.company из DetailedView для того, через что вы проходите при создании объекта DetailView, и правда в том, что они не совпадают, в при создании объекта было скопировано только значение этого момента, поэтому, если вы измените self.company из MainGui, это не изменит self.company из DetailView.

Реструктурируя свой проект, вы получаете следующее:

#!/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()  
person eyllanesc    schedule 04.04.2018
comment
Спасибо, ты мой герой! Это меня многому научило! - person CodingCat; 04.04.2018