Уведомления

Группа в Telegram: @pythonsu

#1 Авг. 15, 2018 08:14:53

man1985
Зарегистрирован: 2018-08-14
Сообщения: 11
Репутация: +  0  -
Профиль   Отправить e-mail  

Checkbox внутри QTableView не изменяет своего состояния при нажатии на него

Использую 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

Офлайн

#2 Авг. 15, 2018 10:59:53

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 9727
Репутация: +  843  -
Профиль   Отправить e-mail  

Checkbox внутри QTableView не изменяет своего состояния при нажатии на него

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(). Скорее всего, ты не знаешь про многие срабатывания, которые включают флажок обратно.



Офлайн

#3 Авг. 15, 2018 12:32:55

Rodegast
От: Пятигорск
Зарегистрирован: 2007-12-28
Сообщения: 2683
Репутация: +  182  -
Профиль   Отправить e-mail  

Checkbox внутри QTableView не изменяет своего состояния при нажатии на него

 data1=super(EditableSqlModel,self).data(index,QtCore.Qt.DisplayRole)
QtCore.Qt.CheckStateRole



С дураками и сектантами не спорю, истину не ищу.
Ели кому-то правда не нравится, то заранее извиняюсь.

Офлайн

#4 Авг. 15, 2018 12:56:33

man1985
Зарегистрирован: 2018-08-14
Сообщения: 11
Репутация: +  0  -
Профиль   Отправить e-mail  

Checkbox внутри QTableView не изменяет своего состояния при нажатии на него

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 

Офлайн

#5 Авг. 15, 2018 12:57:25

man1985
Зарегистрирован: 2018-08-14
Сообщения: 11
Репутация: +  0  -
Профиль   Отправить e-mail  

Checkbox внутри QTableView не изменяет своего состояния при нажатии на него

Rodegast
QtCore.Qt.CheckStateRole
Не помогло

Офлайн

#6 Авг. 15, 2018 21:25:04

Rodegast
От: Пятигорск
Зарегистрирован: 2007-12-28
Сообщения: 2683
Репутация: +  182  -
Профиль   Отправить e-mail  

Checkbox внутри QTableView не изменяет своего состояния при нажатии на него

> Не помогло

А чего ты хотел? QSqlQueryModel просто отображает данные, для того что бы их редактировать тебе нужно:
1) Переопределить метод setData у модели так что бы в в нём происходила запись в БД.
2) Создать делегату и повесить её на соответствующий столбец
3) В делегате вызывать редактор, когда редактор закрыт, вызывать из делегаты метод модели setData и передавать туду дынные для изменения.



С дураками и сектантами не спорю, истину не ищу.
Ели кому-то правда не нравится, то заранее извиняюсь.

Офлайн

#7 Авг. 16, 2018 11:28:17

man1985
Зарегистрирован: 2018-08-14
Сообщения: 11
Репутация: +  0  -
Профиль   Отправить e-mail  

Checkbox внутри QTableView не изменяет своего состояния при нажатии на него

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)?

Офлайн

#8 Авг. 16, 2018 16:15:18

Rodegast
От: Пятигорск
Зарегистрирован: 2007-12-28
Сообщения: 2683
Репутация: +  182  -
Профиль   Отправить e-mail  

Checkbox внутри QTableView не изменяет своего состояния при нажатии на него

> Делегата не нужна для редактирования, при нажатии на чекбокс в setData передаются данные соответствующие инвертированному состоянию чекбокса

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

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

Этот сигнал нужен только для того что бы виджет смог при необходимости узнать что данные могли поменяться. Что бы про это узнала модель нужно вызывать что то вроде beginResetModel / endResetModel



С дураками и сектантами не спорю, истину не ищу.
Ели кому-то правда не нравится, то заранее извиняюсь.

Отредактировано Rodegast (Авг. 17, 2018 02:12:07)

Офлайн

#9 Авг. 20, 2018 11:45:59

man1985
Зарегистрирован: 2018-08-14
Сообщения: 11
Репутация: +  0  -
Профиль   Отправить e-mail  

Checkbox внутри QTableView не изменяет своего состояния при нажатии на него

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

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version