0

So, I'm using python and PySide2 in Maya for custom tools.

I have a model (QTableModel), and a QTableView.

every row in the model will have a bunch of information and a checkbox.

I got to do a QItemDelegate and use it as a check box. That is fine. I having trouble getting if that delegate is Checked or not.

I iterate thru the model getting its data (to store it inside a Maya scene in a node)

    data = []
    rows = self.rowCount(1) #self is the model in this snnipet
    for row in range(rows):
        array = []
        for column in range (6): #6 for the fixed number of columns
            info = index.data()
            array.append(index.data())
        data.append(array)

And it happens that the first item in every row is a check box (delegate). In the final data array I end up getting the actual QItemDelegate Object when I really wanted to get its state, checked or not, but it has not an isChecked() method.

Ideas on how to get that?

Thank you very much!

######## EDIT 1

So, the model does have the flag mentioned in the comments:

def flags(self, index):
        return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable

I think i am doing the delegate wrong, as I confess, i found this snnipet on line and I'm trying to understand it.... but it goes like this:

class CBDelegate(QItemDelegate):

def __init__(self, parent):

    QItemDelegate.__init__(self, parent)


def paint(self, painter, option, index):

    self.cb = QCheckBox()

    try:
        self.cb.setChecked(index.data())
    except:
        pass

    if not self.parent().indexWidget(index):
        self.parent().setIndexWidget(index, self.cb)

and then, at the TableView:

 self.setItemDelegateForColumn(0, CBDelegate(self))

Does it help? (if you have maya 2017 i can give you the whole code... it is a kind of a mess as I'm using this as an learning exercise!

Thank you.

############ EDIT 2

Table View:

class Table(QTableView):
    def __init__(self, *args, **kwargs):
        QTableView.__init__(self, *args, **kwargs)

        # Set the delegate for column 0 of our table
        #self.setItemDelegateForColumn(6, ButtonDelegate(self)) #problem for another time
        self.setItemDelegateForColumn(0, CBDelegate(self))

Model:

class Model(QtCore.QAbstractTableModel):

    def __init__(self, cycles = [[]], headers = [], parent = None):
        QtCore.QAbstractTableModel.__init__(self, parent)

        self.cycles = cycles
        self.headers = headers

    def rowCount(self, parent):

        return len(self.cycles)

    def columnCount(self, parent):
        return len(self.cycles[0])

    def flags(self, index):
        return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable

    def data(self, index, role):

        if role == QtCore.Qt.DisplayRole:

            row = index.row()
            column = index.column()
            value = self.cycles[row][column]
            return value

        if role == QtCore.Qt.EditRole:

            row = index.row()
            column = index.column()
            return self.cycles[row][column]

        if (role == QtCore.Qt.TextAlignmentRole):
            return QtCore.Qt.AlignCenter;

    def setData(self, index, value, role = QtCore.Qt.EditRole):

        change = False

        if role == QtCore.Qt.EditRole:

            row = index.row()
            column = index.column()

            value = value

            if (column == 1) or (column == 4):
                try:
                    str(value)
                    change = True
                except:
                    pm.warning("Not a valid name")
                    change = False
            elif (column == 2):
                try:
                    int(value)
                    change = True
                except:
                    pm.warning("Not a valid frame")
                    change = False
            elif (column == 3):
                try:
                    int(value)
                    change = True
                except:
                    pm.warning("Not a valid frame")
                    change = False

            elif (column == 5):
                try:
                    int(value)
                    change = True
                except:
                    pm.warning("Not a valid version number")
                    change = False

            if change:
                self.cycles[row][column] = value
                self.dataChanged.emit(row, column)
                return True

            return False            

    def headerData(self, section, orientation, role):

        if role == QtCore.Qt.DisplayRole:

            if orientation == QtCore.Qt.Horizontal:
                return self.headers[section]

    def insertRows(self, position, rows, values = [] , parent = QtCore.QModelIndex()):


        self.beginInsertRows(parent, position, position+rows-1)

        self.cycles.insert(position, values)

        self.endInsertRows()

        self.getData()



    def getData(self):

        rows = self.rowCount(1)

        data = []

        for row in range(rows):
            array = []
            for column in range (6):
                index = self.index(row, column)
                info = index.data()

                if type(info) == bool:
                    array.append(info)

                elif type(info) == QItemDelegate:
                    val_is_checked = index.data(QtCore.Qt.CheckStateRole) != QtCore.Qt.Unchecked
                    array.append(val_is_checked)

                else:
                    info = str(info)
                    array.append(info)

            array.append("del")
            data.append(array)

        dic = {}
        for item in data:
            dic[item[1]]=item

        for key in dic:
            print key, dic[key]


        #this from pickle
        #newData = data2String(dic)
        # and this is what I wanna save inside Maya
        #pm.setAttr("cycleAnimationListNode.cycles", newData)

The Delegate is in Edit 1, above.

Then I guess you still need the cycles and headers to start this model:

headers = ["Select", "  Cycle Name  ", " Start ", "  End  ", "Info", "Version", " Del "]

cycles = [[False,"idle","1","70","cool information","0", "deleteBtnPlaceHolder"]]

I hope this does it.

Thank you.

##### EDIT 3

I have this custom method in the model:

def getData(self):
        rows = self.rowCount(1)
        data = []
        for row in range(rows):
            array = []
            for column in range (6):
                index = self.index(row, column)
                info = index.data()
                array.append(info)              
            data.append(array)

        dic = {}
        for item in data:
            dic[item[1]]=item

        print ""
        print "data:"
        print ''
        for key in dic:
            print(key, dic[key])

I use it to transforme the model into a dictionary, so i can serialize it and store as an string attribute of a Node inside Autodesk Maya. It runs alright but the information it gets from the first column is always None. Do i have to retrieve it differently?

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Mendel Reis
  • 55
  • 2
  • 14

1 Answers1

1

It is not necessary to use a delegate if you want to show a QCheckBox, you only need to enable the flag Qt::ItemIsUserCheckable properly, in addition you must save the information through setData(), since in your code that information is lost since the stored in the following section shows the model with these modifications:

Update:

Previously disable the return of the data method for the role Qt::DisplayRole for the first column since it placed the text in the table, which for me was unnecessary, now I have enabled it but so that the text is not displayed I have placed a delegate that removes the text

from PySide2.QtWidgets import *
from PySide2.QtCore import *

class Model(QAbstractTableModel):
    def __init__(self, cycles = [[]], headers = [], parent = None):
        QAbstractTableModel.__init__(self, parent)
        self.cycles = cycles
        self.headers = headers
        self.values_checked = []

    def rowCount(self, parent):
        return len(self.cycles)

    def columnCount(self, parent):
        return len(self.cycles[0])

    def flags(self, index):
        fl = Qt.ItemIsEnabled | Qt.ItemIsSelectable 
        if index.column() == 0:
            fl |= Qt.ItemIsUserCheckable
        else:
            fl |= Qt.ItemIsEditable
        return fl

    def data(self, index, role):
        if not index.isValid():
            return 
        row = index.row()
        column = index.column()

        if role == Qt.DisplayRole:
            value = self.cycles[row][column]
            return value

        elif role == Qt.TextAlignmentRole:
            return Qt.AlignCenter;

        elif role == Qt.CheckStateRole and column==0:
            return Qt.Checked if self.cycles[row][column] else Qt.Unchecked


    def setData(self, index, value, role = Qt.EditRole):
        change = False
        row = index.row()
        column = index.column()

        if role == Qt.CheckStateRole:
            value =  value != Qt.Unchecked
            change = True
        if role == Qt.EditRole:
            if (column == 1) or (column == 4):
                try:
                    str(value)
                    change = True
                except:
                    pm.warning("Not a valid name")
                    change = False
            elif (column == 2):
                try:
                    int(value)
                    change = True
                except:
                    pm.warning("Not a valid frame")
                    change = False
            elif (column == 3):
                try:
                    int(value)
                    change = True
                except:
                    pm.warning("Not a valid frame")
                    change = False

            elif (column == 5):
                try:
                    int(value)
                    change = True
                except:
                    pm.warning("Not a valid version number")
                    change = False
        if change:
            self.cycles[row][column] = value
            self.dataChanged.emit(row, column)
            return True
        return False            

    def headerData(self, section, orientation, role):
        if role == Qt.DisplayRole:
            if orientation == Qt.Horizontal:
                return self.headers[section]

    def insertRows(self, position, rows, values = [] , parent =QModelIndex()):
        self.beginInsertRows(parent, position, position+rows-1)
        self.cycles.insert(position, values)
        self.endInsertRows()
        self.getData()

    def roleNames(self):
        roles = QAbstractTableModel.roleNames(self)
        roles["Checked"] = Qt.CheckStateRole
        return roles


    def getData(self):
            rows = self.rowCount(1)
            data = []
            for row in range(rows):
                array = []
                for column in range (6):
                    index = self.index(row, column)
                    info = index.data()
                    array.append(info)              
                data.append(array)

            dic = {}
            for item in data:
                dic[item[1]]=item

            print("")
            print("data:")
            print('')
            for key in dic:
                print(key, dic[key])


class EmptyDelegate(QStyledItemDelegate):
    def paint(self, painter, option, index):
        opt = QStyleOptionViewItem(option)
        self.initStyleOption(opt, index)
        opt.text = ""
        QApplication.style().drawControl(QStyle.CE_ItemViewItem, opt, painter)


if __name__ == '__main__':
    import sys

    app = QApplication(sys.argv)
    w = QTableView()
    w.setItemDelegateForColumn(0, EmptyDelegate(w))
    headers = ["Select", "  Cycle Name  ", " Start ", "  End  ", "Info", "Version", " Del "]
    cycles = [[True,"idle","1","70","cool information","0", "deleteBtnPlaceHolder"]]
    model = Model(cycles, headers)
    w.setModel(model)
    w.show()
    sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • This is going well. the only problem, yet is thi: set thie method as a custom method of the model : – Mendel Reis Dec 30 '17 at 19:18
  • def getData(self): rows = self.rowCount(1) data = [] for row in range(rows): array = [] for column in range (6): index = self.index(row, column) info = index.data() array.append(info) data.append(array) dic = {} for item in data: dic[item[1]]=item print "" print "data:" print '' for key in dic: print(key, dic[key]) – Mendel Reis Dec 30 '17 at 19:19
  • and the information i get for the checkbox cell is None... even though it says it is changing for true or false in the setData method... – Mendel Reis Dec 30 '17 at 19:19
  • @MendelReis Now update the answer and correct those errors. – eyllanesc Dec 30 '17 at 19:50
  • @MendelReis The updated code is in https://gist.github.com/eyllanesc/a4caf9b474d8e0c21df3a7d3c2a54a2c, do not forget to mark my answer as correct – eyllanesc Dec 30 '17 at 20:34
  • @MendelReis My final code does not show True or False, check it in the previous link. – eyllanesc Dec 30 '17 at 20:35
  • Yeah, weird, it shows the checkbox (working nicely), but next to it it writes true or false, depending on the checkBox. which is funny cause it is written true or false, no capital letters. I just copied and pasted your code... Also, one question, why the empty delegate class at the end? where are you using it? – Mendel Reis Dec 31 '17 at 01:38
  • @MendelReis I am using it in the following line: https://gist.github.com/eyllanesc/a4caf9b474d8e0c21df3a7d3c2a54a2c#file-main-py-L138, and I just use it to not see that text. – eyllanesc Dec 31 '17 at 01:42
  • Man, forget about it... that was it. I set the empty delegate to the column and it worked, my mistake,, I knew something was off with that emptyDelegate.. i forgot the set it at the tableView. Well. thank you for guiding me thru this! Now i just gotta align that checkbox and do the remove row button! But I guess I can figure that out !! Thank you and Happy new year! – Mendel Reis Dec 31 '17 at 01:45
  • @MendelReis Do not forget to mark my answer as correct, if you do not know how to do it check the following link: [tour] – eyllanesc Dec 31 '17 at 01:47
  • Last question: Where can i find more about the the things you sued, like Qt.Uncheked and .CE_ItemViewItem .... did you get all this from the documentation? any nice tutorials you know of? I try to learn coding by myself (and the whole internet) but sometimes it is like learning french from a dictionary! – Mendel Reis Dec 31 '17 at 01:49
  • Hey, If you have the time, I see you also know C++, perhaps you could help me with this. I found this solution to align the check box, but it is in C++, do you think you could help me make it Python? the link is this [link](https://wiki.qt.io/Technical_FAQ#How_can_I_align_the_checkboxes_in_a_view.3F) and you can search for "How can I align the checkboxes in a view?" – Mendel Reis Dec 31 '17 at 02:33
  • @MendelReis code: https://gist.github.com/eyllanesc/ade64a8cd1bd534d473b51a3aec888c5 – eyllanesc Dec 31 '17 at 03:04
  • Damn! that was fast! thank you, I'll compare and try to learn something! – Mendel Reis Dec 31 '17 at 03:06
  • Hey there. If you ever have a minute, please, care to take a look at this question [link](https://stackoverflow.com/questions/48103030/qabstratcttablemodel-removerows) Thank you. – Mendel Reis Jan 04 '18 at 20:34