QStandardItem checkbox changes reflected on object in Pyside

1.5k Views Asked by At

How can I sync the checkboxes in the UI with the properties they are representing? So when I change the checkbox state in the UI it properly sets the value of the property on the class object to match the checkbox state?

For example if i click the Continue Sim checkbox for FumeFX_001 it should also change the property to match.

The print button is there to help test if the values are being updated as the users click the checkbox in the ui.

enter image description here

import sys
from PySide import QtGui, QtCore
import pprint

class Operation(object):
    def __init__(self, name='', script='', enabled=False):
        self.name = name
        self.script = script
        self.enabled = enabled

class FumeFX(object):
    def __init__(self, name):
        self.name = name
        self.cache = 'Z:/pipeline/max/release/test/tools/cache/####.aur'
        self.operations = [
            Operation('Sim', 'Z:/pipeline/sim.py', True),
            Operation('Continue Sim', 'Z:/pipeline/cont_sim.py', False),
            Operation('Wavelet', 'Z:/pipeline/wavelet.py', False),
            Operation('Continue Wavelet', 'Z:/pipeline/cont_wavelet.py', False),
            Operation('Post', 'Z:/pipeline/post.py', False),
            Operation('Continue Post', 'Z:/pipeline/cont_post.py', False)
        ]

class Phoenix(object):
    def __init__(self, name):
        self.name = name
        self.cache = 'Z:/pipeline/max/release/test/tools/cache/####.aur'
        self.operations = [
            Operation('Bounce', 'Z:/pipeline/sim.py', True),
            Operation('Continue Bounce', 'Z:/pipeline/cont_sim.py', False),
            Operation('Delete', 'Z:/pipeline/wavelet.py', False),
        ]

class Browser(QtGui.QDialog):
    def __init__(self, parent=None):
        super(Browser, self).__init__(parent)

        self.initUI()

    def initUI(self):
        self.resize(400, 400)
        self.setWindowTitle('Assets')

        self.items_model = QtGui.QStandardItemModel()
        self.ui_items = QtGui.QTreeView()
        self.ui_items.setAlternatingRowColors(True)
        self.ui_items.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
        self.ui_items.header().setResizeMode(QtGui.QHeaderView.ResizeToContents)
        self.ui_items.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
        self.ui_items.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
        self.ui_items.setModel(self.items_model)

        self.ui_print = QtGui.QPushButton('Print')

        grid = QtGui.QVBoxLayout()
        grid.setContentsMargins(5, 5, 5, 5)
        grid.addWidget(self.ui_items)
        grid.addWidget(self.ui_print)
        self.setLayout(grid)

        self.nodes = [
            FumeFX('FumeFX_001'),
            FumeFX('FumeFX_002'),
            Phoenix('Phoenix')
        ]

        self.update_model()

        self.ui_print.clicked.connect(self.print_info)

    def print_info(self):
        for n in self.nodes:
            print n.name, n.cache
            # pprint.pprint(vars(n))
            for p in n.operations:
                pprint.pprint(vars(p))
            print '\n'

    def update_model(self):
        model = self.ui_items.model()
        model.clear()
        model.setHorizontalHeaderLabels(['Assets',''])

        # Create Data
        for x in self.nodes:
            root = []

            # Node Name
            n_main = QtGui.QStandardItem()
            n_main.setData(x.name, role=QtCore.Qt.DisplayRole)
            n_main.setData(x, role=QtCore.Qt.UserRole)
            root.append(n_main)

            # Node Output
            n_output = QtGui.QStandardItem()
            n_output.setData(x.cache, role=QtCore.Qt.DisplayRole)
            n_output.setData(x, role=QtCore.Qt.UserRole)
            root.append(n_output)

            # Operations
            for op in x.operations:
                row = []

                op_name = QtGui.QStandardItem()
                op_name.setData(op.name, role=QtCore.Qt.DisplayRole)
                op_name.setData(op, role=QtCore.Qt.UserRole)
                row.append(op_name)

                op_enabled = QtGui.QStandardItem()
                op_enabled.setData(QtCore.Qt.Checked, role=QtCore.Qt.CheckStateRole)
                op_enabled.setData(op, role=QtCore.Qt.UserRole)
                op_enabled.setCheckable(True)
                row.append(op_enabled)

                n_main.appendRow(row)

            # add node to main treeview
            model.appendRow(root)

        self.ui_items.expandAll()

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    ex = Browser()
    ex.show()
    sys.exit(app.exec_())
1

There are 1 best solutions below

1
On BEST ANSWER

Firstly, you must initialize the checkboxes with the default values:

# Operations
for op in x.operations:
    row = []
    ...
    op_enabled = QtGui.QStandardItem()
    op_enabled.setData(op, role=QtCore.Qt.UserRole)
    op_enabled.setCheckable(True)
    if op.enabled:
        op_enabled.setCheckState(QtCore.Qt.Checked)
    else:
        op_enabled.setCheckState(QtCore.Qt.Unchecked)
    row.append(op_enabled)

Then you can connect a slot to the itemChanged signal of the model, which will update the values whenever the checked-state is changed:

class Browser(QtGui.QDialog):
    ...    
    def initUI(self):
        ...    
        self.ui_print.clicked.connect(self.print_info)

        self.items_model.itemChanged.connect(self.handleItemChanged)

    def handleItemChanged(self, item):
        if item.isCheckable():
            op = item.data(QtCore.Qt.UserRole)
            op.enabled = item.checkState() == QtCore.Qt.Checked