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, exitclass 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)