PySide2 SQLITE TableView - PullRequest
       17

PySide2 SQLITE TableView

1 голос
/ 22 сентября 2019

При попытке отобразить данные из таблицы QSQLITE в QML TableView я наткнулся на этот ответ и попытался его обработать.Я понял необходимость реализации data() и roleNames(), но у меня есть проблема, когда дело доходит до QVariant, которого нет в PySide2.Я получаю NameError: name 'QVariant' is not defined.Я проверил чтение данных из базы данных, она работает.Есть ли какой-либо тип данных, который можно использовать вместо QVariant в PySide2?Мой план состоит в том, чтобы иметь несколько источников данных из разных таблиц. В этом примере используется setContextProperty, лучше ли в этом случае использовать qmlRegisterType?

База данных SQLITE

структура папок

├── ViewModel
│   └── QCond.py
├── Qml
│   └── Modellist.qml
└── qmlengine.py

qmlengine.py

import os
import sys
from PySide2.QtCore import QUrl, QStringListModel, QCoreApplication, Qt
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine, qmlRegisterType
from PySide2.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel
from ViewModel.QCond import *
if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()
    db = QSqlDatabase.addDatabase("QSQLITE")
    db.setDatabaseName("C:\\Users\\terao\\Documents\\ctmd\\CatData.db")
    db.open()
    qry = QSqlQuery()
    qry=db.exec_("SELECT tip,s FROM uzad")
    tabmodel = QtCond()
    tabmodel.setQuery(qry)
    engine.rootContext().setContextProperty("tabmodel", tabmodel)
    engine.load(QUrl.fromLocalFile('Qml/Modellist.qml'))
    if not engine.rootObjects():
        sys.exit(-2)
    sys.exit(app.exec_())

QCond.py

import sys
from PySide2 import QtCore, QtGui, QtQml
from PySide2.QtCore import QObject, Qt,Signal, Slot, QUrl, QStringListModel, QCoreApplication
import sqlite3
from PySide2.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel
class QtCond(QSqlQueryModel):
    def __init__(self):
        super(QtCond, self).__init__()

    def roleNames(self):
        roles = {
            Qt.UserRole + 1 : 'tip',
            Qt.UserRole + 2 : 's'
        }
        return roles

    def data(self, index, role):
        if role < Qt.UserRole:
            # caller requests non-UserRole data, just pass to papa
            return super(QtTabModel, self).data(index, role)

        # caller requests UserRole data, convert role to column (role - Qt.UserRole -1) to return correct data
        return super(QtTabModel, self).data(self.index(index.row(), role - Qt.UserRole -1), Qt.DisplayRole)

    @QtCore.Slot(result=QVariant)  # don't know how to return a python array/list, so just use QVariant
    def roleNameArray(self):
        # This method is used to return a list that QML understands
        list = []
        # list = self.roleNames().items()
        for key, value in self.roleNames().items():
            list.append(str(value))

        return QVariant(list)

Modellist.qml

import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls 2.5

TableView {
    width: 1000
    height: 1000
    model: tabmodel
    TableViewColumn {
        role: "tip" // case-sensitive, must match a role returned by roleNames()
    }
    TableViewColumn {
        role: "s"
    }

}

Любая помощь приветствуется.Заранее спасибо.

1 Ответ

1 голос
/ 23 сентября 2019

В вашем случае roleNameArray возвращает список, и, как я отмечаю в в этом ответе , вы должны использовать "QVariantList".С другой стороны, я улучшил пример, обобщив пример на основе моих старых ответов: ( 1 ), ( 2 ), учитывая, что решение:

├── db
│   └── CatData.db
├── qml
│   └── Modellist.qml
├── qmlengine.py
└── ViewModel
    └── model.py

ViewModel / model.py

from PySide2 import QtCore, QtSql


class SqlQueryModel(QtSql.QSqlQueryModel):
    def data(self, index, role=QtCore.Qt.DisplayRole):
        value = None
        if index.isValid():
            if role < QtCore.Qt.UserRole:
                value = super(SqlQueryModel, self).data(index, role)
            else:
                columnIdx = role - QtCore.Qt.UserRole - 1
                modelIndex = self.index(index.row(), columnIdx)
                value = super(SqlQueryModel, self).data(
                    modelIndex, QtCore.Qt.DisplayRole
                )
        return value

    def roleNames(self):
        roles = dict()
        for i in range(self.record().count()):
            roles[QtCore.Qt.UserRole + i + 1] = self.record().fieldName(i).encode()
        return roles

    @QtCore.Slot(result="QVariantList")
    def roleNameArray(self):
        names = []
        for i in range(self.record().count()):
            names.append(self.record().fieldName(i))
        return names

qml / Modellist.qml

import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls 2.5

ApplicationWindow{
    width: 1000
    height: 1000
    visible: true

    TableView {
        anchors.fill: parent
        model: tabmodel

        /*TableViewColumn {
            role: "tip"
        }
        TableViewColumn {
            role: "s"
        }*/

        Component.onCompleted: {
            var roles = model.roleNameArray()
            for (var i=0; i<roles.length; i++) {
              var column = addColumn( Qt.createQmlObject(
                "import QtQuick.Controls 1.1; TableViewColumn {}",
                this) )
              column.role = roles[i]
              column.title = roles[i]
            }
        }
    }
}

qmlengine.py

import os
import sys

from PySide2 import QtCore, QtGui, QtSql, QtQml

from ViewModel.model import SqlQueryModel

def create_connection(path):
    db = QtSql.QSqlDatabase.addDatabase('QSQLITE')
    db.setDatabaseName(path)
    if not db.open():
        print('''Unable to establish a database connection.\n
            This example needs SQLite support. Please read
            the Qt SQL driver documentation for information
            how to build it.\n\n Click Cancel to exit.''')
        return False
    return True

if __name__ == "__main__":
    current_dir = os.path.dirname(os.path.realpath(__file__))
    app = QtGui.QGuiApplication(sys.argv)
    engine = QtQml.QQmlApplicationEngine()
    db_path = os.path.join(current_dir, "db", "CatData.db")
    if not create_connection(db_path):
        sys.exit(-1)

    tabmodel = SqlQueryModel()
    tabmodel.setQuery("SELECT tip,s FROM uzad")
    engine.rootContext().setContextProperty("tabmodel", tabmodel)
    qml_path = os.path.join("qml", "Modellist.qml")
    engine.load(QtCore.QUrl.fromLocalFile(qml_path))
    if not engine.rootObjects():
        sys.exit(-2)
    sys.exit(app.exec_()) 

Вывод:

enter image description here


Целью qmlRegisterType является регистрация типов Python / C ++ в QMLабстрагируя происхождение и реализацию.В вашем случае, если вы хотите зарегистрировать его, лучше, чтобы запрос был qproperty, чтобы к нему можно было обращаться из QML:

ViewModel / model.py

from PySide2 import QtCore, QtSql


class SqlQueryModel(QtSql.QSqlQueryModel):

    queryStrChanged = QtCore.Signal(str)

    def get_query_str(self):
        return self.query().lastQuery()

    def set_query_str(self, query):
        if self.get_query_str() == query:
            return
        self.setQuery(query)
        self.queryStrChanged.emit(query)

    query_str = QtCore.Property(
        str, fget=get_query_str, fset=set_query_str, notify=queryStrChanged
    )

    def data(self, index, role=QtCore.Qt.DisplayRole):
        value = None
        if index.isValid():
            if role < QtCore.Qt.UserRole:
                value = super(SqlQueryModel, self).data(index, role)
            else:
                columnIdx = role - QtCore.Qt.UserRole - 1
                modelIndex = self.index(index.row(), columnIdx)
                value = super(SqlQueryModel, self).data(
                    modelIndex, QtCore.Qt.DisplayRole
                )
        return value

    def roleNames(self):
        roles = dict()
        for i in range(self.record().count()):
            roles[QtCore.Qt.UserRole + i + 1] = self.record().fieldName(i).encode()
        return roles

    @QtCore.Slot(result="QVariantList")
    def roleNameArray(self):
        names = []
        for i in range(self.record().count()):
            names.append(self.record().fieldName(i))
        return names

qml / Modellist.qml

import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls 2.5

import mycomponents 1.0

ApplicationWindow{
    width: 1000
    height: 1000
    visible: true

    SqlQueryModel{
        id: tabmodel
        query_str: "SELECT tip,s FROM uzad"
    }

    TableView {
        anchors.fill: parent
        model: tabmodel

        /*TableViewColumn {
            role: "tip"
        }
        TableViewColumn {
            role: "s"
        }*/

        Component.onCompleted: {
            var roles = model.roleNameArray()
            for (var i=0; i<roles.length; i++) {
              var column = addColumn( Qt.createQmlObject(
                "import QtQuick.Controls 1.1; TableViewColumn {}",
                this) )
              column.role = roles[i]
              column.title = roles[i]
            }
        }
    }
}

qmlengine.py

import os
import sys

from PySide2 import QtCore, QtGui, QtSql, QtQml

from ViewModel.model import SqlQueryModel

def create_connection(path):
    db = QtSql.QSqlDatabase.addDatabase('QSQLITE')
    db.setDatabaseName(path)
    if not db.open():
        print('''Unable to establish a database connection.\n
            This example needs SQLite support. Please read
            the Qt SQL driver documentation for information
            how to build it.\n\n Click Cancel to exit.''')
        return False
    return True

if __name__ == "__main__":
    current_dir = os.path.dirname(os.path.realpath(__file__))
    QtQml.qmlRegisterType(SqlQueryModel, "mycomponents", 1, 0, "SqlQueryModel")
    app = QtGui.QGuiApplication(sys.argv)
    engine = QtQml.QQmlApplicationEngine()
    db_path = os.path.join(current_dir, "db", "CatData.db")
    if not create_connection(db_path):
        sys.exit(-1)
    qml_path = os.path.join("qml", "Modellist.qml")
    engine.load(QtCore.QUrl.fromLocalFile(qml_path))
    if not engine.rootObjects():
        sys.exit(-2)
    sys.exit(app.exec_())
...