[PyQt] Segfault when trying to rely on QStyledItemDelegate.setEditorData/ModelData default behavior
Elvis Stansvik
elvstone at gmail.com
Thu Feb 11 20:23:46 GMT 2016
2016-02-11 21:14 GMT+01:00 Elvis Stansvik <elvstone at gmail.com>:
> I found the problem, I had forgot to change the user property from
> dict to QVariant (was testing around).
>
> With the property of type QVariant, it seems to work fine.
>
> And sorry that the example was so broken apart from that (missing
> imports, syntax errors, ...) I was in a bit of a hurry to compile it
> together from my actual code.
>
> Here's the fixed one:
>
> https://gist.github.com/estan/c051d1f798c4c46caa7d
>
> See attached screenshot for how it looks. I can now edit cells which
> contain str -> int dicts using a bunch of pop-out sliders. Yay.
And to clarify, I'm obviously not planning on having the dict
visualised as str(..) like it is now, that was just for testing :)
Elvis
>
> Elvis
>
> 2016-02-11 17:52 GMT+01:00 Elvis Stansvik <elvstone at gmail.com>:
>> Hi all,
>>
>> I'm working on a custom table model (inheriting QAbstractTableModel),
>> where I'll be using custom delegates + editors for some columns.
>>
>> As an experiment, I've tried to create a column where the item data is
>> a dict (wrapped in MyCoolItem), and made a MyCoolDelegate which
>> returns my custom editor (the DisplayRole is just the dict as a string
>> for now).
>>
>> The attached screenshot shows the look.
>>
>> The problem is that when I double click an item in the first column,
>> to bring up my custom editor, I get a segfault when
>> QStyledItemDelegate::setEditorData tries to write the user property
>> from my editor (the default behavior of
>> QStyledItemDelegate.setEditorData is to use the user property on the
>> editor as storage for the data during editing).
>>
>> The code and backtrace follows:
>>
>> Code:
>>
>> from sys import argv, exit
>>
>> from PyQt5.QtCore import (Qt, QAbstractTableModel, QModelIndex, pyqtProperty,
>> pyqtSignal, QVariant)
>> from PyQt5.QtWidgets import (QApplication, QWidget,
>> QStyledItemDelegate, QTableView)
>>
>>
>> class MyAbstractItem(object):
>>
>> def data(self, role):
>> raise NotImplementedError
>>
>> def setData(self, value, role):
>> raise NotImplementedError
>>
>>
>> class MyTextItem(MyAbstractItem):
>>
>> def __init__(self, text):
>> self._text = text
>>
>> def data(self, role):
>> if role != Qt.DisplayRole:
>> return None
>>
>> return self._text
>>
>> def setData(self, text, role):
>> if role != Qt.EditRole:
>> return False
>>
>> self._text = text
>>
>> return True
>>
>>
>> class MyCoolItem(MyAbstractItem):
>>
>> def __init__(self, levels):
>> self._levels = levels #: dict: Maps names to values
>>
>> def data(self, role):
>> if role == Qt.DisplayRole:
>> return str(self._levels) # Just as string for now.
>> elif role == Qt.EditRole:
>> return QVariant(self._levels)
>>
>> return None
>>
>> def setData(self, levels, role):
>> if role != Qt.EditRole:
>> return False
>>
>> self._levels = levels.value()
>>
>> return True
>>
>>
>> class MyCoolEditor(QWidget):
>>
>> changed = pyqtSignal()
>>
>> def __init__(self, parent=None):
>> super(MyCoolEditor, self).__init__(parent)
>> self._values = None
>>
>> @pyqtProperty(dict, user=True)
>> def values(self):
>> return self._values
>>
>> @values.setter
>> def values(self, values):
>> self._values = values
>>
>> layout = QFormLayout(self)
>>
>> for name, value in values.items():
>> slider = QSlider(Qt.Horizontal)
>> slider.setMinimum(0)
>> slider.setMaximum(100)
>> slider.setValue(value)
>> slider.valueChanged.connect(partial(self._setLevel, name))
>> layout.addRow(name + ':', slider)
>>
>> self.setLayout(layout)
>>
>> def _setValue(self, name, value):
>> self._values[name] = value
>> self.changed.emit()
>>
>>
>> class MyCoolDelegate(QStyledItemDelegate):
>>
>> def __init__(self, parent=None):
>> super(MyCoolDelegate, self).__init__(parent)
>>
>> def paint(self, painter, option, index):
>> super(MyCoolDelegate, self).paint(painter, option, index)
>>
>> def sizeHint(self, option, index):
>> super(MyCoolDelegate, self).sizeHint(option, index)
>>
>> def createEditor(self, parent, option, index):
>> levels = index.data(Qt.EditRole)
>>
>> if isinstance(levels, dict):
>> editor = MyCoolEditor(parent)
>> editor.changed.connect(self._endEditing)
>> return editor
>>
>> return super(MyCoolDelegate, self).createEditor(parent, option, index)
>>
>> def _endEditing(self):
>> editor = self.sender()
>> self.commitData.emit(editor)
>> self.closeEditor.emit(editor)
>>
>>
>> class MyModel(QAbstractTableModel):
>>
>> def __init__(self, headers, rows, parent=None):
>> super(MyModel, self).__init__(parent)
>>
>> self._headers = headers
>> self._rows = rows
>>
>> def rowCount(self, parent=QModelIndex()):
>> if parent.isValid():
>> return 0
>>
>> return len(self._rows)
>>
>> def columnCount(self, parent=QModelIndex()):
>> return len(self._headers)
>>
>> def headerData(self, section, orientation, role=Qt.DisplayRole):
>> if orientation != Qt.Horizontal or role != Qt.DisplayRole:
>> return None
>>
>> return self._headers[section]
>>
>> def data(self, index, role=Qt.DisplayRole):
>> if not index.isValid():
>> return None
>>
>> # Delegate to the item at the given index.
>> return self._rows[index.row()][index.column()].data(role)
>>
>> def setData(self, index, value, role=Qt.EditRole):
>> if not index.isValid():
>> return False
>>
>> # Delegate to the item at the given index.
>> if self._rows[index.row()][index.column()].setData(value, role):
>> self.dataChanged.emit(index, index)
>> return True
>>
>> return False
>>
>> def flags(self, index):
>> if not index.isValid():
>> return Qt.NoItemFlags
>>
>> return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable
>>
>>
>> app = None
>>
>>
>> def main():
>> global app
>>
>> app = QApplication(argv)
>>
>> model = MyModel(
>> headers=['Cool Column', 'Text Column'],
>> rows=[
>> [MyCoolItem({'a': 10, 'b': 20}), MyTextItem('c')],
>> [MyCoolItem({'d': 30, 'e': 50}), MyTextItem('f')]
>> ],
>> parent=app
>> )
>>
>> delegate = MyCoolDelegate(model)
>>
>> view = QTableView()
>> view.setItemDelegateForColumn(0, delegate)
>> view.setModel(model)
>> view.show()
>>
>> exit(app.exec_())
>>
>>
>> if __name__ == '__main__':
>> main()
>>
>> Backtrace:
>>
>> (gui-demo)estan at newton:~$ gdb python
>> GNU gdb (Ubuntu 7.10-1ubuntu2) 7.10
>> Copyright (C) 2015 Free Software Foundation, Inc.
>> License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
>> This is free software: you are free to change and redistribute it.
>> There is NO WARRANTY, to the extent permitted by law. Type "show copying"
>> and "show warranty" for details.
>> This GDB was configured as "x86_64-linux-gnu".
>> Type "show configuration" for configuration details.
>> For bug reporting instructions, please see:
>> <http://www.gnu.org/software/gdb/bugs/>.
>> Find the GDB manual and other documentation resources online at:
>> <http://www.gnu.org/software/gdb/documentation/>.
>> For help, type "help".
>> Type "apropos word" to search for commands related to "word"...
>> Reading symbols from python...(no debugging symbols found)...done.
>> (gdb) run test.py
>> Starting program: /home/estan/orexplore/pyenv/gui-demo/bin/python test.py
>> [Thread debugging using libthread_db enabled]
>> Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
>> [New Thread 0x7fffeaba3700 (LWP 23551)]
>>
>> Program received signal SIGSEGV, Segmentation fault.
>> 0x0000000000493416 in PyDict_SetItem ()
>> (gdb) bt
>> #0 0x0000000000493416 in PyDict_SetItem ()
>> #1 0x00000000004cf691 in _PyObject_GenericSetAttrWithDict ()
>> #2 0x000000000042945b in ?? ()
>> #3 0x00000000004bae16 in PyEval_EvalFrameEx ()
>> #4 0x00000000004b7986 in PyEval_EvalCodeEx ()
>> #5 0x00000000004d3bb9 in ?? ()
>> #6 0x00000000004a4516 in PyObject_CallFunction ()
>> #7 0x00007ffff66ba44a in ?? () from
>> /usr/lib/python2.7/dist-packages/PyQt5/QtCore.so
>> #8 0x00007ffff66ba692 in ?? () from
>> /usr/lib/python2.7/dist-packages/PyQt5/QtCore.so
>> #9 0x00007ffff6294534 in QMetaProperty::write(QObject*, QVariant
>> const&) const () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
>> #10 0x00007ffff62bceef in QObject::setProperty(char const*, QVariant
>> const&) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
>> #11 0x00007ffff220494b in QStyledItemDelegate::setEditorData(QWidget*,
>> QModelIndex const&) const () from
>> /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
>> #12 0x00007ffff27714c6 in ?? () from
>> /usr/lib/python2.7/dist-packages/PyQt5/QtWidgets.so
>> #13 0x00007ffff217f6ae in ?? () from
>> /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
>> #14 0x00007ffff217f8c8 in ?? () from
>> /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
>> #15 0x00007ffff217fb5c in QAbstractItemView::edit(QModelIndex const&,
>> QAbstractItemView::EditTrigger, QEvent*) () from
>> /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
>> #16 0x00007ffff2732b79 in ?? () from
>> /usr/lib/python2.7/dist-packages/PyQt5/QtWidgets.so
>> #17 0x00007ffff21838ec in
>> QAbstractItemView::mouseDoubleClickEvent(QMouseEvent*) () from
>> /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
>> #18 0x00007ffff2731feb in ?? () from
>> /usr/lib/python2.7/dist-packages/PyQt5/QtWidgets.so
>> #19 0x00007ffff1f5edf7 in QWidget::event(QEvent*) () from
>> /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
>> #20 0x00007ffff205d85e in QFrame::event(QEvent*) () from
>> /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
>> #21 0x00007ffff218338b in QAbstractItemView::viewportEvent(QEvent*) ()
>> from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
>> #22 0x00007ffff273243b in ?? () from
>> /usr/lib/python2.7/dist-packages/PyQt5/QtWidgets.so
>> #23 0x00007ffff6285b6c in
>> QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject*,
>> QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
>> #24 0x00007ffff1f1b9bc in QApplicationPrivate::notify_helper(QObject*,
>> QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
>> #25 0x00007ffff1f215a9 in QApplication::notify(QObject*, QEvent*) ()
>> from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
>> #26 0x00007ffff280b30e in ?? () from
>> /usr/lib/python2.7/dist-packages/PyQt5/QtWidgets.so
>> #27 0x00007ffff6285d7b in QCoreApplication::notifyInternal(QObject*,
>> QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
>> #28 0x00007ffff1f204b2 in
>> QApplicationPrivate::sendMouseEvent(QWidget*, QMouseEvent*, QWidget*,
>> QWidget*, QWidget**, QPointer<QWidget>&, bool) () from
>> /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
>> #29 0x00007ffff1f78f6b in ?? () from
>> /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
>> #30 0x00007ffff1f7b52b in ?? () from
>> /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
>> #31 0x00007ffff1f1b9dc in QApplicationPrivate::notify_helper(QObject*,
>> QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
>> #32 0x00007ffff1f20ea6 in QApplication::notify(QObject*, QEvent*) ()
>> from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
>> #33 0x00007ffff280b30e in ?? () from
>> /usr/lib/python2.7/dist-packages/PyQt5/QtWidgets.so
>> #34 0x00007ffff6285d7b in QCoreApplication::notifyInternal(QObject*,
>> QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
>> #35 0x00007ffff1969958 in
>> QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent*)
>> () from /usr/lib/x86_64-linux-gnu/libQt5Gui.so.5
>> #36 0x00007ffff196b2b5 in
>> QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent*)
>> () from /usr/lib/x86_64-linux-gnu/libQt5Gui.so.5
>> #37 0x00007ffff194f228 in
>> QWindowSystemInterface::sendWindowSystemEvents(QFlags<QEventLoop::ProcessEventsFlag>)
>> () from /usr/lib/x86_64-linux-gnu/libQt5Gui.so.5
>> #38 0x00007fffedb870b0 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5XcbQpa.so.5
>> #39 0x00007ffff4f5dff7 in g_main_context_dispatch () from
>> /lib/x86_64-linux-gnu/libglib-2.0.so.0
>> #40 0x00007ffff4f5e250 in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
>> #41 0x00007ffff4f5e2fc in g_main_context_iteration () from
>> /lib/x86_64-linux-gnu/libglib-2.0.so.0
>> #42 0x00007ffff62dc4ef in
>> QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>)
>> () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
>> #43 0x00007ffff628350a in
>> QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from
>> /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
>> #44 0x00007ffff628b5ec in QCoreApplication::exec() () from
>> /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
>> #45 0x00007ffff27d3fbb in ?? () from
>> /usr/lib/python2.7/dist-packages/PyQt5/QtWidgets.so
>> #46 0x00000000004ba14a in PyEval_EvalFrameEx ()
>> #47 0x00000000004bf10f in PyEval_EvalFrameEx ()
>> #48 0x00000000004b7986 in PyEval_EvalCodeEx ()
>> #49 0x00000000004e8f3f in ?? ()
>> #50 0x00000000004e3b02 in PyRun_FileExFlags ()
>> #51 0x00000000004e22e6 in PyRun_SimpleFileExFlags ()
>> #52 0x0000000000490fe1 in Py_Main ()
>> #53 0x00007ffff7811a40 in __libc_start_main (main=0x4909f0 <main>,
>> argc=2, argv=0x7fffffffdda8, init=<optimized out>, fini=<optimized
>> out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdd98) at
>> libc-start.c:289
>> #54 0x0000000000490919 in _start ()
>> (gdb) c
>> Continuing.
>> [Thread 0x7fffeaba3700 (LWP 23551) exited]
>>
>> Program terminated with signal SIGSEGV, Segmentation fault.
>> The program no longer exists.
>> (gdb)
>>
>>
>> Anyone know what might be going wrong here? Note that I've neglected
>> to implement setEditorData and setModelData in my custom delegate
>> (MyCoolDelegate), instead trying to rely on their default behavior,
>> which is (from QStyleItemDelegate docs):
>>
>> "The default implementation stores the data in the editor widget's
>> user property."
>>
>> for setEditorData, and
>>
>> "The default implementation gets the value to be stored in the
>> data model from the editor widget's user property."
>>
>> So what I've done is set the user property on MyCoolEditor be the dict
>> which it is currently editing (I use the dict wrapped in a QVariant as
>> the EditRole data).
>>
>> (The editor itself is just supposed to be a bunch of sliders for
>> changing the values associated with the keys in the dict.)
>>
>> I'm really stumped and don't quite know how to debug this, so grateful
>> for any advice.
>>
>> Best regards,
>> Elvis
More information about the PyQt
mailing list