David,

Thanks for your reply.  I was passing a QObject (a QDialog to be exact) as
the parent to the delegate but I wasn't storing a reference to the delegate
anywhere so perhaps it was being deleted in some cases.  I added a variable
to store the reference to the delegate but unfortunately that didn't change
anything.  I pared down the code to it's bare minimum and included it here.
Thanks for looking into the problem.

Brian DeWeese

<code start of BugTest.py>

#!/usr/bin/env python

import sys
from PyQt4 import QtGui, QtCore

_qvChecked = QtCore.QVariant(QtCore.Qt.Checked)
_qvUnchecked = QtCore.QVariant(QtCore.Qt.Unchecked)

##------------------------------------------------------------------------------
class BTObject(object):

   def __init__(self, enabled=False, foo='', bar=0):
       object.__init__(self)
       self._enabled = enabled
       self._foo = foo
       self._bar = bar

   def isEnabled(self):
       return self._enabled

   def setEnabled(self, b=True):
       self._enabled = b

   def createInlineEditor(self, parent):
       return BTObject.InlineEditor(self, parent)

   def __repr__(self):
       return 'BTObject(enabled='+str(self._enabled)+',
foo=\"'+str(self._foo)+'\", bar='+str(self._bar)+')'

   class InlineEditor(QtGui.QWidget):

       _MUTE = 'MUTE'

       def __init__(self, btobject, parent):
           QtGui.QWidget.__init__(self, parent)
           self._btobject = btobject

           self.setAutoFillBackground(True)
           lo = QtGui.QHBoxLayout()
           lo.setMargin(0)
           lo.setSpacing(4)

           self._cbFoo = QtGui.QComboBox()
           for x in ["ABC", "DEF", "GHI", "JKL"]:
               self._cbFoo.addItem(x)

           self._leBar = QtGui.QLineEdit(str(btobject._bar), self)
           self._leBar.setValidator(QtGui.QIntValidator(0, 999999, self))

           lo.addWidget(self._cbFoo, 3)
           lo.addSpacing(5)
           lo.addWidget(QtGui.QLabel('Bar:'))
           lo.addWidget(self._leBar, 3)
           lo.addStretch(5)
           self.setLayout(lo)

           # set the object data into the gui

self._cbFoo.setCurrentIndex(self._cbFoo.findText(self._btobject._foo))
           self._leBar.setText(str(self._btobject._bar))

       def accept(self):
           text = str(self._cbFoo.currentText())
           self._btobject._foo = text
           self._btobject._bar = int(self._leBar.text())
           print 'accept: btobject='+repr(self._btobject)

       def reject(self):
           pass
##>--------------------------------------------------------------------------<##
class BTModel(QtCore.QAbstractTableModel):

   def __init__(self, parent=None ):
       QtCore.QAbstractTableModel.__init__(self, parent)
       self._items = [BTObject(foo="ABC", bar=1),
                      BTObject(foo="DEF", bar=2),
                      BTObject(foo="GHI", bar=3)]
       self._headerData = (QtCore.QVariant("Name"), QtCore.QVariant
("repr"))

   def columnCount(self, parentIndex):
       return len(self._headerData)

   def flags(self, index):
       if not index.isValid():
           return QtCore.Qt.ItemIsEnabled

       if index.column() == 0:
           return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable |
QtCore.Qt.ItemIsUserCheckable
       elif index.column() == 1:
           return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable |
QtCore.Qt.ItemIsEditable
       return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable

   def getItemAt(self, row):
       if row >= 0 and row < len(self._items):
           return self._items[row]
       return None

   def indexOfItem(self, item):
       return self._items.index(item)

   def headerData(self, section, orientation, role):
       if orientation == QtCore.Qt.Horizontal and role in (
QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
           return self._headerData[section]

       return QtCore.QVariant()

   def rowCount(self, parentIndex):
       return len(self._items)

   def setData(self, index, value, role):
       if index.isValid():
           if index.column() == 0 and role == QtCore.Qt.CheckStateRole:
               state = value.toInt()[0] # int value stored as a tuple,
where's that documented?
               btobject = self._items[index.row()]
               btobject.setEnabled(state == QtCore.Qt.Checked)

               # Force a repaint of the entire row.
               index2 = self.createIndex(index.row(), 1)
               self.emit(QtCore.SIGNAL('dataChanged(const QModelIndex &,
const QModelIndex &)'), index2, index2)

       return True

   def data( self, index, role ):
       if not index.isValid():
           return QtCore.QVariant()

       if role == QtCore.Qt.DisplayRole:
           col = index.column()
           if col == 0:
               return QtCore.QVariant(self._items[index.row()]._foo)
           elif col == 1:
               return QtCore.QVariant(repr(self._items[index.row()]))

       elif role == QtCore.Qt.CheckStateRole:
           if index.column() == 0:
               retVal = _qvUnchecked
               btobject = self._items[index.row()]
               if btobject.isEnabled():
                   retVal = _qvChecked
               return retVal

       return QtCore.QVariant()

##>--------------------------------------------------------------------------<##
class BTItemDelegate(QtGui.QItemDelegate):

   def __init__(self, parent):
       QtGui.QItemDelegate.__init__(self, parent)

   def createEditor(self, parent, option, index):
       if index.column() == 1:
           model = index.model()
           btobject = model.getItemAt(index.row())
           editor = btobject.createInlineEditor(parent)
           return editor
       return QtGui.QItemDelegate.createEditor(self, parent, option, index)

   def setEditorData(self, editor, index):
       ''' I don't need to do anything here because I passed in the object
           being edited when the editor was constructed.
       '''
       pass

   def setModelData(self, editor, model, index):
       editor.accept()

##>--------------------------------------------------------------------------<##
class BTEditor(QtGui.QDialog):
   def __init__(self, parent=None):
       QtGui.QDialog.__init__(self, parent)
       self.setWindowTitle('BTObject Editor')

       # Create a button box for the dialog containing the Ok and Cancel
buttons
       buttonBox = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok
                                        | QtGui.QDialogButtonBox.Cancel);
       QtCore.QObject.connect(buttonBox, QtCore.SIGNAL('accepted()'),
self.accept)
       QtCore.QObject.connect(buttonBox, QtCore.SIGNAL('rejected()'),
self.reject)

       # The tree view widget
       self._view = BTEditor.TableView(self)
       self._view.setMinimumSize(QtCore.QSize(300, 100))

       self._delegate = BTItemDelegate(self)


#----------------------------------------------------------------------#
       # If you comment out the setItemDelegat calls than the checkable
       # column will work correctly.  If setItemDelegate is uncommented
       # than the custom editor will work but it breaks the checkable
       # column. If setItemDelegateForColumn is uncommented than the
       # checkable column works correctly but my editor is never used
       # either.

#----------------------------------------------------------------------#
       self._view.setItemDelegate(self._delegate)
       #self._view.setItemDelegateForColumn(1, self._delegate) # this
doesn't work

       self._model = BTModel()
       self._view.setModel(self._model)

       # The final layout, putting it all together
       gl = QtGui.QGridLayout()
       gl.addWidget(self._view   , 1, 0, 1, 2)
       gl.addWidget(buttonBox    , 2, 0, 1, 2)
       self.setLayout(gl)


##------------------------------------------------------------------------##
   class TableView(QtGui.QTableView):
       def __init__(self, parent):
           QtGui.QTableView.__init__(self, parent)
           self.verticalHeader().hide()
           self.setAlternatingRowColors(True)
           self.setEditTriggers(QtGui.QAbstractItemView.DoubleClicked |
                                QtGui.QAbstractItemView.EditKeyPressed)
           self.setGridStyle(QtCore.Qt.NoPen)
           self.setLineWidth(0)
           self.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
           self.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
           self.horizontalHeader().setStretchLastSection(True)
           self.horizontalHeader().setResizeMode(
QtGui.QHeaderView.ResizeToContents )
           self.verticalHeader().setResizeMode(
QtGui.QHeaderView.ResizeToContents )

       def sizeHint(self):
           return QtCore.QSize(600, 100)


if __name__ == "__main__":
   app = QtGui.QApplication(sys.argv)
   win = BTEditor()
   win.show()
   app.connect(app, QtCore.SIGNAL('lastWindowClosed()'), app, QtCore.SLOT
('quit()'))
   sys.exit(app.exec_())


</code>

Brian DeWeese

On 6/5/07, David Boddie <[EMAIL PROTECTED]> wrote:

On Fri, 1 Jun 2007 09:56:25 -0500, Brian DeWeese wrote:

> I have a 2 column QTableView where column 0 is checkable and column 1 is
> editable. I've written a custom editor by implement QItemDelegate which
is
> working fine. Also, the checkbox in column 0 is also working fine. But
> not both at the same time.
>
> If I use view.setItemDelegate(myDelegate) than my delegate is called to
> create my custom editor and everything about column 1 works correctly.
But
> column 0 doesn't work correctly. It is displaying a checkbox with the
> correct current value but clicking on it does not call my model's
setData()
> method or do anything at all as far as I can tell.

OK. This doesn't sound right.

> If I use view.setItemDelegateForColumn(1, myDelegate) than the checkbox
in
> colum 0 works but double-clicking on column 1 will ignore my delegate
and
> create a default editor.

Did you create the delegate in a way that stops it from being garbage
collected and subsequently deleted on the C++ side? In other words,
did you store the instance somewhere, or create it with a parent QObject?

I'm guessing that you did, otherwise you wouldn't see your editor in the
previous situation. :-/

> Is this a known bug in either PyQt or Qt itself? Or am I doing something
> wrong?

I would like to see more code before declaring something wrong with
setItemDelegateForColumn() in either Qt or PyQt.

> I'm using PyQt 4.1.1 with Qt 4.2 on SUSE 10.1. (BTW, Is there a proper
way
> to verify that I'm using the versions that I think I'm using?)

from PyQt4 import pyqtconfig
hex(pyqtconfig._pkg_config["pyqt_version"])
hex(pyqtconfig._pkg_config["qt_version"])

> Here is my model.flags() method.
>
> def flags(self, index):
> if not index.isValid():
> return QtCore.Qt.ItemIsEnabled
>
> if index.column() == 0:
> return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable |
> QtCore.Qt.ItemIsUserCheckable

You might want to make this editable, too.

> elif index.column() == 1:
> return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable |
> QtCore.Qt.ItemIsEditable
>
> return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable

Hope this helps,

David

_______________________________________________
PyQt mailing list    [email protected]
http://www.riverbankcomputing.com/mailman/listinfo/pyqt

_______________________________________________
PyQt mailing list    [email protected]
http://www.riverbankcomputing.com/mailman/listinfo/pyqt

Reply via email to