Найти - Пользователи
Полная версия: Checkbox внутри QTableView не изменяет своего состояния при нажатии на него
Начало » Python для экспертов » Checkbox внутри QTableView не изменяет своего состояния при нажатии на него
1
man1985
Использую PyQt
Делается связка DB -> model -> QTableView
model - перегружаю класс QSqlQueryModel.
В методе flags выставляю флаги flags |=ItemIsUserCheckable, чтоб появился чекбокс в ячейке.
В методе data для DisplayRole возвращаю None, чтоб видеть только чекбокс во 2-м столбце и не видеть данные.
Чекбокс при нажатии на него не меняет своего состояния.
Как сделать, чтоб чекбокс менял состояние при нажатии на него?

Аналогичный код в QtCreator на С++ работает

editablesqlmodel.py

 from PyQt5 import QtCore, QtGui, QtWidgets, QtSql
class EditableSqlModel(QtSql.QSqlQueryModel):
    def __init__(self):
        super().__init__()
    def flags(self, index):
        flags = super(EditableSqlModel, self).flags(index)
        if index.column() in (0,1):
          flags |= QtCore.Qt.ItemIsEditable
        elif (index.column() == 2):
          #flags |= QtCore.Qt.ItemIsUserCheckable 
          flags |=   QtCore.Qt.ItemIsUserCheckable 
        return flags
    
    def data(self,index, role=QtCore.Qt.DisplayRole):
        if not index.isValid():
            return None
        data=super(EditableSqlModel,self).data(index,role)
        if index.column()==2:
            if role != QtCore.Qt.DisplayRole:
              data1=super(EditableSqlModel,self).data(index,QtCore.Qt.DisplayRole)
              val = QtCore.Qt.Checked  if data1 else QtCore.Qt.Unchecked 
              return val
            else:
              return None
            
        else:
            return data
 

zone_db.py
 import sys
from PyQt5.QtWidgets import QApplication, QAction, QWidget
from PyQt5 import QtCore, QtGui, QtWidgets, QtSql
from PyQt5 import QtGui, QtCore
from editablesqlmodel import EditableSqlModel
import connection
from PyQt5.uic import loadUi
class excelSort(QtCore.QSortFilterProxyModel):
    def setValues(self,mdl):
        print ("start setval")
        print(mdl.columnCount())
        self.dflt=[{}]
        print ("кол-во колонок %d" % mdl.columnCount())
        print ("кол-во строк %d" % mdl.rowCount())
        for i in range(mdl.columnCount()):           
            self.dflt.insert(i,{}) # словарь с признаком сортировки
            for j in range(mdl.rowCount()):              
              #print(mdl.data(mdl.index(j,i),QtCore.Qt.DisplayRole))              
              self.dflt[i][mdl.data(mdl.index(j,i),QtCore.Qt.DisplayRole)]=False
        #self.dflt[1].pop("33UVB05EJ003")
        print(self.dflt[1],"dflt[1]")
        print ("exit setval")
    def filterAcceptsRow (self, source_row, source_parent):
        #one of our children might be accepted, so accept this row if one of our children are accepted.
        source_index = self.sourceModel().index(source_row, 0, source_parent)
        #col = source_index.column()
        row = source_index.row()
        #print("row ",row)
        for i in range( self.sourceModel().columnCount()):
            #print(self.sourceModel().data(self.sourceModel().index(row,i),QtCore.Qt.DisplayRole))
            if self.sourceModel().data(self.sourceModel().index(row,i),QtCore.Qt.DisplayRole) in self.dflt[i]:
                return True
        
        #print("exit_filter")
        return False
class mw(QWidget):
    def __init__(self):
      super().__init__()
      self.wd=loadUi('mainwindow.ui')
      self.wd.show()      
    def algs(self):
      print("start algs")  
      self.model=EditableSqlModel()
      tpl=(("кодAlg","кодAlg","кодAlg"),
           ("alg","alg","alg"),
           ("кодalg","name","addv"))
      try:
          self.model.setPK(tpl)
      except Exception as err:
          print(err)      
      try:
          self.model.setQuery('SELECT КодAlg,NAME,AddV FROM Alg',self.db)
          if self.model.lastError().isValid():
              print(self.model.lastError().text())          
          self.model.setHeaderData(1, QtCore.Qt.Horizontal, "Алгоритм")
          self.model.setHeaderData(2, QtCore.Qt.Horizontal, "Пометка")
          self.model.query()
          
          #self.wd.tableView.setModel(self.model)
          #self.flt=QtCore.QSortFilterProxyModel()
          self.flt=excelSort()
          self.flt.setSourceModel(self.model)
          self.flt.setValues(self.model)
          self.wd.tableView.setModel(self.flt)
          self.wd.tableView.setSortingEnabled(True)
          
          #print(self.model.record(1).value(1))
          self.wd.tableView.show()
          
      except Exception as err:
          print(err)
      return True
    def createMenus(self):
      msgAction = QAction("Алгоритмы",self.wd.menuBar)    
      msgAction.triggered.connect(self.algs)   
      self.wd.menuBar.addAction(msgAction)
      
      
      
def main(args):
    app=QApplication(sys.argv)
    mw1=mw()
    mw1.createMenus()
    db_file = r'''D:\Zone_4block_prog_06_11_17.accdb'''
    mw1.db=connection.createConnection(db_file)
    if not mw1.db:
        sys.exit(1)
    sys.exit(app.exec_())
    
if __name__ == '__main__':
     main(sys.argv)
connection.py
 import pyodbc
from PyQt5.QtSql import QSqlQueryModel,QSqlDatabase,QSqlQuery
def createConnection(db_file):
    try:
        user = ''
        password = ''
        odbc_conn_str = 'DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=%s;' %\
                (db_file)
        db = QSqlDatabase.addDatabase("QODBC")
        db.setDatabaseName("DRIVER=Microsoft Access Driver (*.mdb, *.accdb);FIL=MS Access;DBQ=d:\\QtPrj\\Zone_db\\Zone_test.accdb")
        #-----------------------
        if not db.open():
          print("Can't open database")
          return None     
        #conn = pyodbc.connect(odbc_conn_str)
        return db
    except Exception as err:
        print(err)
        return None
py.user.next
man1985
  
    def data(self,index, role=QtCore.Qt.DisplayRole):
        if not index.isValid():
            return None
        data=super(EditableSqlModel,self).data(index,role)
        if index.column()==2:
            if role != QtCore.Qt.DisplayRole:
              data1=super(EditableSqlModel,self).data(index,QtCore.Qt.DisplayRole)
              val = QtCore.Qt.Checked  if data1 else QtCore.Qt.Unchecked 
              return val
            else:
              return None
            
        else:
            return data
В этом методе сделай вывод всех ветвей в консоль через print(). Скорее всего, ты не знаешь про многие срабатывания, которые включают флажок обратно.
Rodegast
 data1=super(EditableSqlModel,self).data(index,QtCore.Qt.DisplayRole)
QtCore.Qt.CheckStateRole
man1985
py.user.next
В этом методе сделай вывод всех ветвей в консоль через print()
     def data(self,index, role=QtCore.Qt.DisplayRole):
        print("-------------method data--------role=",role)
        if not index.isValid():
            return None
        data=super(EditableSqlModel,self).data(index,role)
        if index.column()==2:
            if role != QtCore.Qt.DisplayRole:
              data1=super(EditableSqlModel,self).data(index,QtCore.Qt.CheckStateRole)
              val = QtCore.Qt.Checked  if data1 else QtCore.Qt.Unchecked
              print(" Value val=%d index.row=%d index.col=%d " % (val,index.row(),index.column())) 
              return val
            else:
              print(" return None index.row=%d index.col=%d " % (index.row(),index.column()))   
              return None
            
        else:
            print(" return Data index.row=%d index.col=%d " % (index.row(),index.column()))
            return data
вывод при нажатии на чекбокс
 -------------method data--------role= 10
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 6
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 7
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 9
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 10
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 1
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 0
 return None index.row=115 index.col=2 
-------------method data--------role= 8
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 6
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 7
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 9
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 10
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 1
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 0
 return None index.row=115 index.col=2 
-------------method data--------role= 8
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 10
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 6
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 7
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 9
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 10
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 1
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 0
 return None index.row=115 index.col=2 
-------------method data--------role= 8
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 0
 return Data index.row=115 index.col=0 
-------------method data--------role= 6
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 7
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 9
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 10
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 1
 Value val=0 index.row=115 index.col=2 
-------------method data--------role= 0
 return None index.row=115 index.col=2 
-------------method data--------role= 8
 Value val=0 index.row=115 index.col=2 
man1985
Rodegast
QtCore.Qt.CheckStateRole
Не помогло
Rodegast
> Не помогло

А чего ты хотел? QSqlQueryModel просто отображает данные, для того что бы их редактировать тебе нужно:
1) Переопределить метод setData у модели так что бы в в нём происходила запись в БД.
2) Создать делегату и повесить её на соответствующий столбец
3) В делегате вызывать редактор, когда редактор закрыт, вызывать из делегаты метод модели setData и передавать туду дынные для изменения.
man1985
Rodegast
А чего ты хотел? QSqlQueryModel просто отображает данные, для того что бы их редактировать тебе нужно:
1) Переопределить метод setData у модели так что бы в в нём происходила запись в БД.
2) Создать делегату и повесить её на соответствующий столбец
3) В делегате вызывать редактор, когда редактор закрыт, вызывать из делегаты метод модели setData и передавать туду дынные для изменения.
Переопределил метод setData,запись в БД -происходит
Делегата не нужна для редактирования, при нажатии на чекбокс в setData передаются данные соответствующие инвертированному состоянию чекбокса
Но у меня теперь новая проблема: после записи в БД, данные в TableView не обновляются сами.
В доках написано, что нужно принудительно выпустить сигнал dataChanged (const ::QModelIndex&,const ::QModelIndex&), но обновление данных в TableView всё равно не происходит
Чтобы данные всё-таки обновились, пробую повторно запустить запрос на выборку, но прилетает ошибка:
 Query to ex: SELECT КодAlg,NAME,AddV FROM Alg
[Microsoft][Диспетчер драйверов ODBC] Ошибка последовательности функций QODBC3: Unable to execute statement
Вот метод setData:
     def setData(self, index, value, role):
        if not index.isValid():
            return None
        ok=False
        print("start SetData")
        row=index.row()
        col = index.column()
        primaryKeyIndex = self.index(row, self.pktn['keyIdx'][col])
        id1 = super(EditableSqlModel, self).data(primaryKeyIndex)
        query=QtSql.QSqlQuery()
        str1="update " + self.pktn['table'][col] + " set " + self.pktn['valName'][col] + \
                  " = '" + str(value) + "' where " + self.pktn['keyName'][col] + " = " + \
                  str(id1)
        query.prepare(str1)
        ok = query.exec_()
        if not ok:
            print(query().lastError().text())
            return False        
        self.dataChanged.emit(index,index)
        #self.dataChanged(index,index)
        str1=self.query().executedQuery()
        print("Query to ex:",str1)
        self.setQuery(str1)        
        ok=self.query().exec_()
        if not ok:
            print(self.query().lastError().text())
            return False
        return ok
Наверно, повторная выборка не лучший вариант для быстродействия, но всё же как хотя бы её запустить в методе setData?
Как заставить отработать dataChanged.emit(index,index)?
Rodegast
> Делегата не нужна для редактирования, при нажатии на чекбокс в setData передаются данные соответствующие инвертированному состоянию чекбокса

Обычно её всё равно определяют.

> Как заставить отработать dataChanged.emit(index,index)?

Этот сигнал нужен только для того что бы виджет смог при необходимости узнать что данные могли поменяться. Что бы про это узнала модель нужно вызывать что то вроде beginResetModel / endResetModel
man1985
Rodegast
Этот сигнал нужен только для того что бы виджет смог при необходимости узнать что данные могли поменяться. Что бы про это узнала модель нужно вызывать что то вроде beginResetModel / endResetModel
beginResetModel / endResetModel - не помогло
Вопрос закрыт:
Следующий код обновляет модель:
         str1=self.query().executedQuery()
        self.setQuery(str1)  
Но следующий далее код вызывал ошибку и обновления не было:
         ok=self.query().exec_()
        if not ok:
            print(self.query().lastError().text())
            return False
        return ok
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB