PyQt5 QListView прекратил загрузку записей из QSqlTableModel, и я не знаю почему - PullRequest
0 голосов
/ 12 октября 2019

Обновленная информация внизу поста.

Мне нужно немного помочь с PyQt5 и его архитектурой представления модели для баз данных.

Я пытаюсь повторносделать проект базы данных я сделал. (https://github.com/DivineHermit/Media-Database-v1/) обновление его для использования моделей (https://github.com/DivineHermit/Media-Database-v2/).

В настоящее время у меня есть три QSqlTableModel по одному для каждой таблицы в базе данных:

1) model_media - это QSqlTableModel из 'таблица media в моей базе данных и в настоящее время имеет 616 записей и используется QListView в главном окне и QDataWidgetMapper для отображения подробной информации о выбранной записи.

2) model_genres другая QSqlTableModel для «жанров»таблица.

3) model_types окончательная QSqlTableModel для таблицы 'media_types'.

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

Пояснение (Надеюсь): Исходная модель model_media используется для отображения списка заголовков мультимедиа в QListView (в настоящее времяимеет 616 записей) он будет отображать первые сто или около того записей в QListView и при прокрутке вниз будет загружаться больше, как и ожидалось. Поэтому я перешел к реализации следующих двух моделей и аналогичным образом отобразил их данные в своих собственных окнах, что сработало нормально, однако в этот момент основной QListView больше не загружал записи при прокрутке вниз по списку, он отображает только первую сотню или около тогозаписи.

Оригинальный проект (PyQt5 & Sqlite3): https://github.com/DivineHermit/Media-Database-v1/

Новая версия (только PyQt5): https://github.com/DivineHermit/Media-Database-v2/

Обновление: тестовые сценарии.

В настоящее время я считаю, что способ, которым я реализовал свой класс / GUI, влияет на основную модель и будет сводить проект к основам и пытаться провести некоторое тестирование, чтобы найти истинную проблему.

Я включаю тестовые сценарии, приведенные ниже, в надежде, что они будут полезны другим.

Следуя совету eyllanesc, я написал минимальный пример моделей, которые я использую, и провел его по базе данных, пытаясь воспроизвестиошибка, однако, этот тестовый скрипт на самом деле работал:

from PyQt5 import QtCore
from PyQt5 import QtSql
from PyQt5 import QtWidgets


class DBQueries(QtWidgets.QMainWindow):
    def __init__(self, db="Media-Database.db", parent=None):
        super(DBQueries, self).__init__(parent)
        self.db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
        self.db.setDatabaseName(db)
        if not self.db.open():
            print("Error connecting to database:\n{}".format(self.db.lastError().text()))
        # ===== Media Model =====
        self.model_media = QtSql.QSqlTableModel(self)
        self.model_media.setTable("media")
        self.model_media.sort(1, QtCore.Qt.AscendingOrder)
        self.model_media.select()
        # ===== Genres Model =====
        self.model_genres = QtSql.QSqlTableModel(self)
        self.model_genres.setTable("genres")
        self.model_genres.setSort(1, QtCore.Qt.AscendingOrder)
        self.model_genres.select()
        # ===== Types Model =====
        self.model_types = QtSql.QSqlTableModel(self)
        self.model_types.setTable("media_types")
        self.model_types.setSort(1, QtCore.Qt.AscendingOrder)
        self.model_types.select()
        # ===== Proxy/Search Model =====
        self.proxy = QtCore.QSortFilterProxyModel(self)
        self.proxy.setSourceModel(self.model_media)
        self.proxy.setFilterKeyColumn(1)
        self.proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
        # ===== UI =====
        self.cw = QtWidgets.QWidget(self)
        self.vl = QtWidgets.QVBoxLayout(self.cw)
        self.le_search = QtWidgets.QLineEdit()
        self.le_search.textChanged.connect(
            lambda: self.proxy.setFilterFixedString(
                self.le_search.text()))
        self.vl.addWidget(self.le_search)

        self.view_media_list = QtWidgets.QListView()
        self.view_media_list.setModel(self.proxy)
        self.view_media_list.setModelColumn(1)  # Title column.
        self.vl.addWidget(self.view_media_list)

        self.view_media = QtWidgets.QTableView()
        self.view_media.setModel(self.proxy)
        self.view_media.sortByColumn(1, QtCore.Qt.AscendingOrder)
        self.vl.addWidget(self.view_media)

        self.cb_genres = QtWidgets.QComboBox()
        self.cb_genres.setModel(self.model_genres)
        self.cb_genres.setModelColumn(1)
        self.vl.addWidget(self.cb_genres)

        self.view_genres = QtWidgets.QTableView()
        self.view_genres.setModel(self.model_genres)
        self.vl.addWidget(self.view_genres)

        self.cb_types = QtWidgets.QComboBox()
        self.cb_types.setModel(self.model_types)
        self.cb_types.setModelColumn(1)
        self.vl.addWidget(self.cb_types)

        self.view_types = QtWidgets.QTableView()
        self.view_types.setModel(self.model_types)
        self.vl.addWidget(self.view_types)

        # ===== Selected =====
        self.selected_media = self.view_media.selectionModel()
        self.selected_media.selectionChanged.connect(self.display_details)
        self.setCentralWidget(self.cw)

    def display_details(self):
        """Display a dialog with table cell contents, this is just a PoC."""
        details = QtWidgets.QDialog(self)
        details.setMinimumWidth(250)
        lbl = QtWidgets.QLabel(details)
        lbl.setText(str(self.selected_media.currentIndex().data()))
        details.show()


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    # Added an input to make t easier to swap
    #  between project and test databases.
    db = input("Enter name of database >>> ")
    window = DBQueries(db)
    window.show()

    sys.exit(app.exec_())

Понимая, что люди, желающие помочь, не будут иметь данныхя использую для проекта я создал следующий скрипт и использовал тестовый скрипт для новой базы данных с таким же положительным / рабочим результатом:

import sqlite3
import random

__author__ = "Dominic Lee"
__version__ = "0.0.1"


class DBGenerator:
    """Used to create an sqlite3 database with random data for testing."""

    def __init__(self, db_name=":memory:", count=1000):
        self.connection = sqlite3.connect(db_name)
        self.cursor = self.connection.cursor()
        self.file_name = db_name
        self.create_media_table()
        self.create_genres_table()
        self.create_types_table()
        self.create_test_entries(count)
        self.create_test_genres()
        self.create_test_types()

    def create_media_table(self):
        """Create the main media table."""
        self.cursor.execute("""
            CREATE TABLE IF NOT EXISTS media (
            id INTEGER PRIMARY KEY, 
            title VARCHAR NOT NULL,
            description VARCHAR,
            age_rating VARCHAR,
            genre VARCHAR,
            season INTEGER,
            disc_count INTEGER,
            media_type VARCHAR,
            play_time INTEGER,
            notes VARCHAR)""")
        self.connection.commit()
        return True

    def create_genres_table(self):
        """Create the genres table."""
        self.cursor.execute("""
            CREATE TABLE IF NOT EXISTS genres (
            id INTEGER PRIMARY KEY,
            genre VARCHAR,
            description VARCHAR,
            examples VARCHAR)""")
        self.connection.commit()
        return True

    def create_types_table(self):
        """Create the media_types table."""
        self.cursor.execute("""
        CREATE TABLE IF NOT EXISTS media_types (
        id INTEGER PRIMARY KEY NOT NULL,
        type VARCHAR)""")
        self.connection.commit()
        return True

    def create_test_entries(self, count=1000):
        """
        Add randomized dummy data to database for testing
        :param count: the number of entries to add.
        :return: True
        """
        for i in range(1, count+1):
            print(f"Adding entry: {str(i).zfill(len(str(count)))}")
            self.cursor.execute("""
            INSERT INTO media VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            """, (None,
                  f"Entry {str(i).zfill(len(str(count)))}",
                  f"Blank Description {str(i).zfill(len(str(count)))}",
                  f"{random.choice(['U: Universal', '15', '18', 'E: Exempt'])}",
                  f"{random.choice(['Action', 'Comedy', 'Drama', 'Sci-Fi'])}",
                  random.randint(0, 10),
                  random.randint(1, 6),
                  f"{random.choice(['DVD: Movie', 'DVD: Series', 'Audio CD', 'PS4 Game'])}",
                  random.randint(60, 1000),
                  f"Starring: {random.choice(['John', 'Paul', 'George', 'Ringo'])}"))
        self.connection.commit()
        return True

    def create_test_genres(self):
        """Add a list of genres to the database for testing purposes."""
        genres = ["Action",
                  "Anime",
                  "Biography",
                  "Cartoon",
                  "Comedy: Action",
                  "Comedy: Quiz Show",
                  "Comedy: Sitcom",
                  "Comedy: Stand-Up",
                  "Cookery",
                  "D.I.Y.",
                  "Documentary",
                  "Drama",
                  "Drama: Criminal",
                  "Educational",
                  "Fantasy",
                  "Game Show",
                  "Gardening",
                  "Horror",
                  "Kids/Children",
                  "Lifestyle",
                  "Makeover",
                  "Music",
                  "Mystery",
                  "News",
                  "Reality TV",
                  "Sci-Fi",
                  "Shopping",
                  "Soap",
                  "Talk Show",
                  "Thriller",
                  "Travel",
                  "Western"]
        for g in genres:
            print(f"Adding genre: {g}")
            self.cursor.execute("""
            INSERT INTO genres VALUES (?, ?, ?, ?)
            """, (None, g, f"Description for {g}", f"Examples for {g}"))
        self.connection.commit()
        return True

    def create_test_types(self):
        """Add media types to the database."""
        types = ["Audio CD",
                 "Book: Educational",
                 "Book: Graphic Novel",
                 "Book: Manga",
                 "Book: Novel",
                 "Book: Programming",
                 "Cassett Tape",
                 "DVD: Movie",
                 "DVD: Series",
                 "Digital Download",
                 "Nintendo DS Game",
                 "Nintendo Wii Game",
                 "PC Game",
                 "PS1 Game",
                 "PS2 Game",
                 "PS3 Game",
                 "PS4 Game",
                 "PSP Game",
                 "PSVita Game"]
        for t in types:
            print(f"Adding media type: {t}")
            self.cursor.execute("""
            INSERT INTO media_types VALUES (?, ?)
            """, (None, t))
        self.connection.commit()
        return True


if __name__ == "__main__":
    # Create an instance of DBGenerator,
    # give it a name and how many entries to create.
    # DBGenerator(db_name, count)
    # db_name defaults to ':memory:'
    # count defaults to 1000
    db = DBGenerator(count=1000)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...