Use delegates to map button's checked-state to a model's boolean?



  • I've been reading/experimenting with the documentation re: delegates & mapping, but I have been unsuccessful adapting it to my situation. I'd like to map the checked-state of a button to a model's bool value. Basically I'd like to eliminate the "update" method from the code below, and have delegates take care of the mapping automatically. Is this even possible? Thanks

    @
    import sys
    from PySide.QtCore import *
    from PySide.QtGui import *
    from PySide.QtSql import *

    class CentralWidget(QWidget):
    def init(self, mainWindow):
    super(CentralWidget, self).init()
    self.setFixedSize(600,400)

        self.vbox = QVBoxLayout()
        self.contentHBox = QHBoxLayout()
        self.widgetGrid = QGridLayout()
    
        self.createDatabase()
        
        self.model = QSqlTableModel()
        self.model.setTable("NeverGonna")
        self.model.dataChanged.connect(lambda: self.update(thingToChange='buttons'))
        self.model.select()
    
        self.buttonDict = {}
        
        self.createWidgets()
    
        self.update(thingToChange='buttons')
        
        self.confirmButton = QPushButton("Show/Hide Results")
        self.confirmButton.clicked.connect(self.confirmResults)
        
        self.view = QTableView()
        self.view.setModel(self.model)
        self.view.hide()
    
        self.contentHBox.addLayout(self.widgetGrid)
        self.contentHBox.addStretch(1)
        
        self.vbox.addLayout(self.contentHBox)
        self.vbox.addWidget(self.confirmButton)
        self.vbox.addWidget(self.view)
        self.vbox.addStretch(1)
    
        self.setLayout(self.vbox)
    
    def createWidgets(self):
        for r in range(self.model.rowCount()):
            rec = self.model.record(r)
            name = rec.value(0)
    
            newLabel = QLabel(name)
            text = QLabel("... is gonna")
    
            self.buttonList=[]
    
            b1Text = self.model.headerData(1, Qt.Horizontal, Qt.DisplayRole)
            b2Text = self.model.headerData(2, Qt.Horizontal, Qt.DisplayRole)
            b3Text = self.model.headerData(3, Qt.Horizontal, Qt.DisplayRole)
    
            button1 = QPushButton(b1Text)
            button2 = QPushButton(b2Text)
            button3 = QPushButton(b3Text)
    
            button1.setCheckable(True)
            button2.setCheckable(True)
            button3.setCheckable(True)
    
            button1.clicked.connect(lambda: self.update(thingToChange='model'))
            button2.clicked.connect(lambda: self.update(thingToChange='model'))
            button3.clicked.connect(lambda: self.update(thingToChange='model'))
    
            self.buttonDict[name] = { b1Text : button1,
                                      b2Text : button2,
                                      b3Text : button3}
    
            self.widgetGrid.addWidget(newLabel, r,0)
            self.widgetGrid.addWidget(text, r, 1)
            self.widgetGrid.addWidget(button1, r,2)
            self.widgetGrid.addWidget(button2, r,3)
            self.widgetGrid.addWidget(button3, r,4)
    
    def update(self, thingToChange='buttons'):
        print("update")
        keyList = sorted(self.buttonDict.keys())
    
        for index, person in enumerate(keyList):
    
            num = index
            for header in self.buttonDict[person].keys():
                button = self.buttonDict[person][header]
    
                if thingToChange == 'buttons':
                    #self.model.select()
                    button.blockSignals(True)
                    if int(self.model.record(num).value(header)) == 1:
                        button.setChecked(True)
                        print("checking button {0} - {1}".format(person, header))
                    else:
                        button.setChecked(False)
                        print("UNchecking button {0} - {1}".format(person, header))
                    button.blockSignals(False)
    
                elif thingToChange == 'model':
                    if button.isChecked():
                        print('Updating Model: {0} - {1} = TRUE (1)'.format(person, header))
                        query = QSqlQuery("UPDATE NeverGonna SET '{h}'='1' WHERE Name='{n}'".format(h=header, n=person))
                    else:
                        print('Updating Model: {0} - {1} = FALSE (0)'.format(person, header))
                        query = QSqlQuery("UPDATE NeverGonna SET '{h}'='0' WHERE Name='{n}'".format(h=header, n=person))
    
                    self.model.select()
                else:   return
    
    def confirmResults(self):
        if self.view.isVisible():
            self.view.hide()
        else:
            self.view.show()
    
    def createDatabase(self):
        db = QSqlDatabase.addDatabase("QSQLITE")
        db.setDatabaseName("ForumQuestionDelegates")
        ok = db.open()
        print(ok)
        query = QSqlQuery("DROP TABLE IF EXISTS NeverGonna")
        query = QSqlQuery("CREATE TABLE NeverGonna (Name VARCHAR(20), GiveYouUp BOOL, LetYouDown BOOL, RunAroundAndDesertYou BOOL)")
        query = QSqlQuery("INSERT INTO NeverGonna (Name, GiveYouUp, LetYouDown, RunAroundAndDesertYou) VALUES('01 - Rick Astley', '0',' 0', '0')")
        query = QSqlQuery("INSERT INTO NeverGonna (Name, GiveYouUp, LetYouDown, RunAroundAndDesertYou)  VALUES('02 - Mick Nastley', '1', '1', '1')")
    

    class MainWindow(QMainWindow):
    def init(self):
    super(MainWindow, self).init()

        self.centralWidget = CentralWidget(self)
    
        self.setCentralWidget(self.centralWidget)
        self.setWindowTitle("Can I use delegates to map button checked-state to model's 'bools' (1/0)?")
        self.show()
    

    def main():
    app = QApplication(sys.argv)
    win = MainWindow()
    app.setStyle('cleanlooks')
    sys.exit(app.exec_())

    if name == 'main':
    main()
    @



  • Hi Alabaster,

    The following is an example I developed a while back for my students which contains two QTableView's with the same model. The first column of each has a button delegate which reflects the boolean status of the associated model index, i.e. checking a button in one table will cause the corresponding button in the other table to also be checked. The code is written for PyQt4 rather than PySide but it shouldn't take too much thought/effort to convert it.

    @
    import sip
    sip.setapi('QString', 2)
    sip.setapi('QVariant', 2)

    from PyQt4.QtCore import *
    from PyQt4.QtGui import *

    class TableModel(QAbstractTableModel):
    def init(self, parent=None, **kwargs):
    QAbstractTableModel.init(self, parent, **kwargs)

        self._data=[[False for _ in xrange(10)] for _ in xrange(10)]
    
    def rowCount(self, parent=QModelIndex()): return len(self._data)
    def columnCount(self, parent=QModelIndex()): return len(self._data[0])
    
    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid(): return None
        if not role==Qt.DisplayRole: return None
    
        return self._data[index.row()][index.column()]
    
    def setData(self, index, value, role=Qt.EditRole):
        if not index.isValid(): return False
        if not role==Qt.EditRole: return False
    
        self._data[index.row()][index.column()]=value
        self.dataChanged.emit(index, index)
        return True
    

    class ButtonDelegate(QItemDelegate):
    def init(self, parent):
    QItemDelegate.init(self, parent)

        self._editors={}
        self._model=None
    
    def paint(self, painter, option, index):
        if self._model==None:
            self._model=self.parent().model()
            self._model.dataChanged.connect(self._modelChanged)
    
        if not self.parent().indexWidget(index):
            row,column=index.row(),index.column()
            button=QPushButton(
                    "({0},{1})".format(row,column),
                    self.parent(), 
                    clicked=lambda: self._setModelData(row, column),
                    checkable=True
                )
    
            self.parent().setIndexWidget(index, button)
            self._editors[(row,column)]=button
    
    def _setModelData(self, row, column):
        button=self._editors[(row,column)]
        model=self.parent().model()
        index=model.index(row,column)
        model.setData(index, button.isChecked())
    
    @pyqtSlot(QModelIndex,QModelIndex)
    def _modelChanged(self, start, end):
        row,column=start.row(),start.column()
        checked=start.data()
        if checked==self._editors[(row,column)].isChecked(): return
        self._editors[(row,column)].setChecked(checked)
    

    class TableView(QTableView):
    def init(self, *args, **kwargs):
    QTableView.init(self, *args, **kwargs)

        self.setItemDelegateForColumn(0, ButtonDelegate(self))
    

    if name=="main":
    from sys import argv, exit

    class Widget(QWidget):
        def __init__(self, parent=None):
            QWidget.__init__(self, parent)
    
            l=QVBoxLayout(self)
    
            self._tm=TableModel(self)
    
            self._tv=TableView(self)
            self._tv.setModel(self._tm)
            l.addWidget(self._tv)
    
            self._tv2=TableView(self)
            self._tv2.setModel(self._tm)
            l.addWidget(self._tv2)
    
    a=QApplication(argv)
    w=Widget()
    w.show()
    w.raise_()
    exit(a.exec_())
    

    @

    I hope this helps with your problem ;o)



  • Thanks for the response -- I'll have to do some translating to PySideify it but I think that's doable. I'll post back if I'm still confused.

    Thanks!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.