После нескольких постов с ответами на C ++, которые не подходят к моему вопросу и которые более запутаны, чем объяснения, я пытаюсь задать здесь вопрос. Я пытаюсь создать подкласс QSqlTableModel, потому что мне нужны логические столбцы с флажками. Полная рабочая программа:

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtSql import *
import sys

class ImportFilter (QDialog):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        print("Welcome to StandardDialog")
        # Load the Window
        #self.ui = uic.loadUi("ImportFilter.ui",self)
        #self.ui.setModal(True)
        self.buttonBox = QDialogButtonBox()
        self.tableView_typeOfValues = QTableView()
        layout = QVBoxLayout()
        layout.addWidget(self.buttonBox)
        layout.addWidget(self.tableView_typeOfValues)
        self.setLayout(layout)

        # Init Environmment
        self.db = createConnection()

        # create Models
        self.setupModel()

        #setup Views
        self.setupView()

        # connect Signals
        self.buttonBox.clicked.connect(self.pushButton_Box_clicked)

        self.show()
        print("<- initUI")

    def setupView(self):
        print("-> setupView")
        self.tableView_typeOfValues.setModel(self.tableView_typeOfValues_Model)
        self.tableView_typeOfValues.setColumnWidth(0,10)
        self.tableView_typeOfValues.setColumnWidth(1,130)
        self.tableView_typeOfValues.setColumnWidth(2,130)
        self.tableView_typeOfValues.setColumnWidth(3,60)
        self.tableView_typeOfValues.setColumnWidth(4,60)
        self.tableView_typeOfValues.setColumnWidth(5,60)
        self.tableView_typeOfValues.setColumnWidth(6,60)

        self.tableView_typeOfValues.hideColumn(0)
        self.tableView_typeOfValues.hideColumn(3)
        print("<- setupView")

    def setupModel(self):
        print("-> setupModel")
        # own model
        self.tableView_typeOfValues_Model = ImportSqlTableModel()
        print("   tables:", self.db.tables())
        print('   Before .setTable("typeOfValue") and select()')
        self.tableView_typeOfValues_Model.info()
        self.tableView_typeOfValues_Model.setTable("typeOfValue")
        self.tableView_typeOfValues_Model.setEditStrategy(QSqlTableModel.OnFieldChange)
        self.tableView_typeOfValues_Model.select()
        print('   After .setTable("typeOfValue") and select()')
        self.tableView_typeOfValues_Model.info()
        self.headerData()
        print("   Table:",self.tableView_typeOfValues_Model.tableName())
        print("   count:",self.tableView_typeOfValues_Model.rowCount())

        self.tableView_typeOfValues_Model.info()
        print("<- setupModel")

    def setupModelQRY(self):
        print("-> setupModel with Query")
        # works so far
        #self.tableView_typeOfValues_Model = QSqlTableModel()  # edit but no checkboxes
        self.tableView_typeOfValues_Model = ImportSqlTableModel(self.db) # no edit
        # SET query
        qry = QSqlQuery(self.db)
        sql = "SELECT ID, name, unit, source, Import, ImportIfZero, visible FROM typeOfValue"
        qry.prepare(sql)
        qry.exec_(sql)
        self.tableView_typeOfValues_Model.setQuery(qry)
        self.tableView_typeOfValues_Model.select()
        print("   Filter:",self.tableView_typeOfValues_Model.filter())
        print("   SELECT:", self.tableView_typeOfValues_Model.selectStatement())
        self.tableView_typeOfValues_Model.setEditStrategy(QSqlTableModel.OnFieldChange)
        print("<- setupModel")

    def headerData(self):
        print("-> headerData")
        self.tableView_typeOfValues_Model.setHeaderData(0,Qt.Horizontal, "ID")
        self.tableView_typeOfValues_Model.setHeaderData(1,Qt.Horizontal, "name")
        self.tableView_typeOfValues_Model.setHeaderData(2,Qt.Horizontal, "unit")
        self.tableView_typeOfValues_Model.setHeaderData(3,Qt.Horizontal, "source")
        self.tableView_typeOfValues_Model.setHeaderData(4,Qt.Horizontal, "Import")
        self.tableView_typeOfValues_Model.setHeaderData(5,Qt.Horizontal, "ImportIfZero")
        self.tableView_typeOfValues_Model.setHeaderData(6,Qt.Horizontal, "visible")
        print("<- headerData")


    ###################################################################################################
    #                        functions
    ###################################################################################################
    def pushButton_Box_clicked(self,signal):
        print("okButtonClicked")
        print("buttonBox_clicked",signal)
        self.tableView_typeOfValues_Model.submitAll()
        self.exitcode = "ok, but not implemented"
        sys.exit()

    def returnCode(self):
        return self.exitcode

#######################################################################################################################
#                                               C L A S S
#######################################################################################################################

class ImportSqlTableModel(QSqlTableModel):
    def __init__(self):
        super(ImportSqlTableModel, self).__init__()
        print("-> ImportSqlTableModel.__init__:")
        self.booleanSet =[4,5,6]
        self.readOnlySet = [1]
        print("   Inside:")
        self.info()
        print("<- ImportSqlTableModel.__init__:")


    def info(self):
        print("-> info")

        print("   ImportSqlTableModel tables inside :", self.database().tables())
        print("   ImportSqlTableModel self.db       :", self.database())
        print("   ImportSqlTableModel self.Table    :", self.tableName())
        print("   ImportSqlTableModel self.rowCount :", self.rowCount())
        print("   ImportSqlTableModel self.lastEror :", self.lastError().text())
        print("<- info")
    def columnCount(self, index):
        count = QSqlTableModel.columnCount(self, index)
        return count

    def dataChanged(self, QModelIndex, QModelIndex_1, Iterable, p_int=None, *args, **kwargs):
        print("-> Datachanged")

    def data(self, index, role=Qt.DisplayRole):
        print("-> ImportSqlModel.data",index, role)
        print("   1row   :", index.row())
        print("   col    :", index.column())
        print("   data   :", self.record().fieldName(index.column()))
        value = super(ImportSqlTableModel, self).data(index)
        print("  value2:",value)

        if index.column() in self.booleanSet:
            if role == Qt.CheckStateRole:
                if value == 2:
                    return QVariant(Qt.Unchecked)
                else:
                    return QVariant(Qt.Checked)
            else:
                QSqlTableModel.data(self, index, role)
        else:
            return QSqlTableModel.data(self, index, role)

    def setData(self, index, value, role=Qt.EditRole):
        # works with changing value, but not saving
        print("-> ImportSqlModel.setData",index,value,role)
        print("   value:", value)
        if not index.isValid():
            return False
        if role == Qt.EditRole:
            print("   = Qt.Editrole")
            QVariant(value)
            print("   Update table")
            self.select()
        if index.column() in self.booleanSet and role == Qt.CheckStateRole:
            print("   checkbox changed!")
            if value == Qt.Checked:
                print("   Qt.Checked")
                return QSqlTableModel.setData(self, index, 2 , Qt.EditRole)
            else:
                print("   not Qt.Checked")
                return QSqlTableModel.setData(self, index, 0 , Qt.EditRole)
        else:
            return QSqlTableModel.setData(self, index, value, role)

    def flags(self, index):
        print("-> ImportSqlModel.flags")
        print("   index.isValid()",index.isValid())
        if not index.isValid():
            return Qt.ItemIsEnabled
        if index.column() in self.booleanSet:
            return Qt.ItemIsUserCheckable  | Qt.ItemIsEnabled #  | Qt.ItemIsSelectable  | Qt.ItemIsEditable
        elif index.column() in self.readOnlySet:
            return Qt.ItemIsSelectable | Qt.ItemIsEnabled
        else:
            return QSqlTableModel.flags(self, index)
        print("<- ImportSqlModel.flags")


#######################################################################################################################
#                                               D E M O F U N C T I O N
#######################################################################################################################

def createConnection():
    db = QSqlDatabase.addDatabase('QSQLITE')
    db.setDatabaseName('memory')
    if not db.open():
        QMessageBox.critical(None, qApp.tr("Cannot open database"),
                             qApp.tr("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."),
                             QMessageBox.Cancel)
        return False

    query = QSqlQuery()
    query.exec_("CREATE TABLE `typeOfValue` (`ID`   INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, "\
    "`name` TEXT NOT NULL,  `unit`  TEXT NOT NULL,`source` TEXT,`import` INTEGER,`importIfZero` INTEGER,"\
    "`visible`  INTEGER);")

    query.exec_('insert into typeOfValue (name, unit, source, import, importIfZero, visible) values '\
                '("Sound", "dB", "live", 0,0,2)')
    query.exec_('insert into typeOfValue (name, unit, source, import, importIfZero, visible) values '\
                '("Flow", "m/min", "live", 0,2,2)')
    query.exec_('insert into typeOfValue (name, unit, source, import, importIfZero, visible) values '\
                '("Vibration", "mm/s", "live", 2,2,2)')
    query.exec_('insert into typeOfValue (name, unit, source, import, importIfZero, visible) values '\
                '("Voltage", "V", "live", 0,0,0)')
    query.exec_('insert into typeOfValue (name, unit, source, import, importIfZero, visible) values '\
                '("Ampere", "A", "live", 2,0,2)')
    return db
#######################################################################################################################
#                                               M A I N
#######################################################################################################################

if __name__ == '__main__':
    createConnection()
    app = QApplication(sys.argv)
    prog = ImportFilter()
    prog.show()
    sys.exit(app.exec_())

Проблема, с которой я столкнулся, заключается в том, что я могу просматривать таблицу, когда использую запрос для ее заполнения:

def setupModel(self):
    print("-> setupModel")
    # SET query
    qry = QSqlQuery(self.gVar.db)
    sql = "SELECT ID, name, unit, source, Import, ImportIfZero, visible FROM typeOfValue"
    qry.prepare(sql)
    qry.exec_(sql)
    self.tableView_typeOfValues_Model.setQuery(qry)

Но я обнаружил ошибку, что это приводит к таблицам только для чтения. В руководстве описано, что этого делать не следует: См. здесь Поэтому я перешел на .setTable("typeOfValue") # = Tablename

        self.tableView_typeOfValues_Model = ImportSqlTableModel(self.gVar.db)
        self.tableView_typeOfValues_Model.setTable("typeOfValue")
   self.tableView_typeOfValues_Model.setEditStrategy(QSqlTableModel.OnFieldChange)

        self.tableView_typeOfValues_Model.select()

Но теперь у меня пустое представление, и если я вызываю self.lastError().text(), я получаю сообщение о том, что таблица не будет найдена, потому что self.database().tables() (вызывается внутри модели) приносит пустой список. Для меня это означает, что база данных не инициализирована правильно, но self.database() приносит Объект PyQt5.QtSql.QSqlDatabase по адресу 0x042C4D30 в качестве результата.

Пожалуйста, может кто-нибудь подскажет, как исправить подкласс QSqlTableModel. Спасибо!

1
Papageno 30 Дек 2017 в 20:04

2 ответа

Лучший ответ

Проблема вызвана флагом Qt.ItemIsEditable, который не включен для столбца логического типа, это необходимо в инструкции:

if index.column() in self.booleanSet and role == Qt.CheckStateRole:
    print("   checkbox changed!")
    if value == Qt.Checked:
        print("   Qt.Checked")
        return QSqlTableModel.setData(self, index, 2 , Qt.EditRole)
    else:
        print("   not Qt.Checked")
        return QSqlTableModel.setData(self, index, 0 , Qt.EditRole)

Поскольку он проверяет, можно ли редактировать поле.

Итак, решение состоит в том, чтобы включить этот флаг, но чтобы избежать написания текста, мы отключим редактор через делегата:

class ReadOnlyDelegate(QItemDelegate):
    def createEditor(self, parent, option, index):
        lb = QLabel(parent)
        return lb

Полный пример:

import sys

from PyQt5.QtCore import QVariant, Qt
from PyQt5.QtSql import QSqlTableModel, QSqlDatabase
from PyQt5.QtWidgets import QApplication, QTableView, QLabel, QItemDelegate


class ImportSqlTableModel(QSqlTableModel):
    def __init__(self, *args, **kwargs):
        super(ImportSqlTableModel, self).__init__(*args, **kwargs)
        self.booleanSet = [4, 5, 6]  # column with checkboxes
        self.readOnlySet = [1]  # columns which must not be changed
        self.setTable("typeOfValue")
        self.setEditStrategy(QSqlTableModel.OnFieldChange)
        self.select()

    def data(self, index, role=Qt.DisplayRole):
        value = super(ImportSqlTableModel, self).data(index)
        if index.column() in self.booleanSet:
            if role == Qt.CheckStateRole:
                return Qt.Unchecked if value == 2 else Qt.Checked
            else:
                return QVariant()
        return QSqlTableModel.data(self, index, role)

    def setData(self, index, value, role=Qt.EditRole):
        if not index.isValid():
            return False
        if index.column() in self.booleanSet:
            if role == Qt.CheckStateRole:
                val = 2 if value == Qt.Unchecked else 0
                return QSqlTableModel.setData(self, index, val, Qt.EditRole)
            else:
                return False
        else:
            return QSqlTableModel.setData(self, index, value, role)

    def flags(self, index):
        if not index.isValid():
            return Qt.NoItemFlags
        if index.column() in self.booleanSet:
            return Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsEditable
        elif index.column() in self.readOnlySet:
            return Qt.ItemIsSelectable | Qt.ItemIsEnabled
        else:
            return QSqlTableModel.flags(self, index)


class ReadOnlyDelegate(QItemDelegate):
    def createEditor(self, parent, option, index):
        lb = QLabel(parent)
        return lb


if __name__ == '__main__':
    app = QApplication(sys.argv)

    db = QSqlDatabase.addDatabase("QSQLITE")
    db.setDatabaseName("/path/of/your_database.db")
    if not db.open():
        sys.exit(-1)
    model = ImportSqlTableModel()
    w = QTableView()
    w.setModel(model)
    for col in model.booleanSet:
        w.setItemDelegateForColumn(col, ReadOnlyDelegate(w))
    w.show()
    sys.exit(app.exec_())
1
eyllanesc 30 Дек 2017 в 18:31

Ошибка в отношении различных QSqlDatabase object была в вызове подкласса QSqlTableModel. Это должно быть assignedModel = YourSubclassedTableModel(db=your_open_database_object), если источником является физический db. Если база данных находится в памяти db.setDatabaseName(':memory:'), то эта база данных является базой данных по умолчанию и будет использоваться внутри модели. В документации об этом не упоминается. В руководстве по Qt вы найдете:

QSqlTableModel :: QSqlTableModel (QObject * parent = Q_NULLPTR, QSqlDatabase db = QSqlDatabase ()) Создает пустой QSqlTableModel и устанавливает родительский объект как parent, а соединение с базой данных - как db. Если db недействителен, будет использоваться соединение с базой данных по умолчанию.

0
Papageno 1 Янв 2018 в 17:25