tools instalar example como python python-3.x pyqt qt5 pyqt5

python - instalar - pyqt5-tools



Llamar a QColorDialog desencadenado desde el editor QStyledItemDelegate produce un bloqueo (0)

este script falla, al cerrar el QColorDialog, después de ser llamado desde una subclase QTableView, se desencadena desde una señal.

from collections import OrderedDict from PyQt5.QtCore import Qt, pyqtSignal from PyQt5.QtGui import QColor,QStandardItem, QStandardItemModel from PyQt5.QtWidgets import ( QAbstractItemView, QApplication, QColorDialog, QComboBox, QDoubleSpinBox, QGridLayout, QStyledItemDelegate, QSpinBox, QTableView, QLineEdit, QWidget ) class Colors(): colors = OrderedDict() colorNames = OrderedDict() for colorName in QColor.colorNames(): color = QColor(colorName) rgb = color.getRgb()[:3] colors[rgb] = color colorNames[rgb] = colorName @classmethod def updateColor(cls, rgbstr): try: rgb, name = cls.str2rgb(rgbstr) except ValueError as e: print(''Colors.updateColor: Invalid color value %s: %s'' % (rgbstr, e)) raise else: color = QColor(*rgb) if rgb not in cls.colors: cls.colors[rgb] = color cls.colors.move_to_end(rgb, last = False) if name: cls.colorNames[rgb] = name return cls.colorLabel(color), color @classmethod def lookupColor(cls, color): rgb = color.getRgb()[:3] name = cls.colorNames.get(rgb, '''') return rgb, name @classmethod def colorLabel(cls, color): rgb, name = cls.lookupColor(color) label = cls.rgb2str(rgb) if name: label += " (%s)" % name return label @staticmethod def rgb2str(rgb): return '',''.join(map(str, rgb)) @staticmethod def rgb2name(rgb): return ''#%02x%02x%02x'' % (rgb) @staticmethod def str2rgb(rgbstr): if not rgbstr: raise ValueError(''Invalid RGB tuple: %r'' % rgbstr) name = '''' if '' '' in rgbstr: rgbstr, name = rgbstr.split('' '', 1) # convert r,g,b string to int tuple rgb = tuple(map(int, rgbstr.split('','')))[:3] # sanity checks if len(rgb) != 3: raise ValueError(''Invalid RGB tuple length: %s'' % str(rgb)) if max(rgb) > 256: raise ValueError(''Invalid values in RGB tuple: %s'' % str(rgb)) if min(rgb) < 0: raise ValueError(''Invalid values in RGB tuple: %s'' % str(rgb)) if name and name[0] == ''('' and name[-1] == '')'': name = name[1:-1] #print(''str2rgb(%s): %s "%s"'' % (rgbstr, rgb, name)) return rgb, name class ModelEditor(): def createEditor(self, parent, option, index): editor = QLineEdit(parent) editor.setFrame(False) return editor def setData(self, editor, value): editor.setText(value) def setEditorData(self, editor, index): self.setData(editor, index.data(Qt.EditRole)) def getData(self, editor): return editor.text() def setModelData(self, editor, model, index): model.setData(index, self.getData(editor), Qt.EditRole) def updateEditorGeometry(self, editor, option, index): editor.setGeometry(option.rect) class IntRange(ModelEditor): def __init__(self, start = None, stop = None, step = None): self.start = start self.stop = stop self.step = step def createEditor(self, parent, option, index): editor = QSpinBox(parent) editor.setFrame(False) if self.start is not None: editor.setMinimum(self.start) if self.stop is not None: editor.setMaximum(self.stop) if self.step is not None: editor.setSingleStep(self.step) return editor def setData(self, editor, value): editor.setValue(value) def getData(self, editor): editor.interpretText() return editor.value() class FloatRange(IntRange): def __init__(self, start = None, stop = None, step = None, decimals = None): self.start = start self.stop = stop self.step = step self.decimals = decimals def createEditor(self, parent, option, index): editor = QDoubleSpinBox(parent) editor.setFrame(False) if self.start is not None: editor.setMinimum(self.start) if self.stop is not None: editor.setMaximum(self.stop) if self.step is not None: editor.setSingleStep(self.step) if self.decimals is not None: editor.setDecimals(self.decimals) return editor class Choice(ModelEditor): def __init__(self, *choices): self.choices = choices def createEditor(self, parent, option, index): editor = QComboBox(parent) editor.setFrame(False) editor.setAutoFillBackground(True) for i, choice in enumerate(self.choices): editor.insertItem(i, choice) return editor def setData(self, editor, value): editor.setCurrentIndex(self.getIndex(value)) def getData(self, editor): return editor.currentText() def getIndex(self, value): return self.choices.index(value) class Bool(Choice): def __init__(self, choices = None): if choices is not None: self.choices = choices else: self.choices = (''false'', ''true'') class Text(ModelEditor): pass class Color(ModelEditor): def __init__(self): self.chooseColor = ''Choose Color...'' self.lastValue = None self.lastColor = None self.itemView = None def createEditor(self, parent, option, index): self.itemView = itemView = parent.parent() print(itemView) editor = QComboBox(parent) editor.setFrame(False) editor.setAutoFillBackground(True) editor.setEditable(True) for i, (rgb, color) in enumerate(Colors.colors.items()): label = Colors.colorLabel(color) editor.insertItem(i, label) editor.setItemData(i, color, Qt.DecorationRole) # add color chooser item editor.insertSeparator(i+1) editor.insertItem(i+2, self.chooseColor) editor.currentIndexChanged[str].connect(self.currentTextChanged) return editor def currentTextChanged(self, value): if value == self.chooseColor: label = Colors.colorLabel(self.lastColor) print(''selectColorDialog:'', label) self.itemView.selectColorDialog.emit() else: try: label, color = Colors.updateColor(value) except ValueError: return else: print(''currentTextChanged: %s, %s'' % (label, color)) self.lastColor = color self.lastValue = label def setData(self, editor, value): print(''Color.setData(%s)'' % value) try: label, color = Colors.updateColor(value) except ValueError: return idx = editor.findText(label) if idx < 0: editor.insertItem(0, label) idx = 0 editor.setCurrentIndex(idx) self.lastValue = label self.lastColor = color if 0: editor.showPopup() def getData(self, editor): value = editor.currentText() print(''Color.getData(%s)'' % value) if value == self.chooseColor and self.lastValue: value = self.lastValue print(''Color.getData(%s)'' % value) try: label, color = Colors.updateColor(value) except ValueError: return self.lastValue = label self.lastColor = color print(''Color.getData(%s) -> %s'' % (value, label)) return label class ItemDelegate(QStyledItemDelegate): def createEditor(self, parent, option, index): cls = index.data(Qt.UserRole) if isinstance(cls, ModelEditor): return cls.createEditor(parent, option, index) else: return super(ItemDelegate, self).createEditor(parent, option, index) def setEditorData(self, editor, index): cls = index.data(Qt.UserRole) if isinstance(cls, ModelEditor): cls.setEditorData(editor, index) else: super(ItemDelegate, self).setEditorData(editor, index) def setModelData(self, editor, model, index): cls = index.data(Qt.UserRole) if isinstance(cls, ModelEditor): cls.setModelData(editor, model, index) else: super(ItemDelegate, self).setModelData(editor, model, index) def updateEditorGeometry(self, editor, option, index): cls = index.data(Qt.UserRole) if isinstance(cls, ModelEditor): cls.updateEditorGeometry(editor, option, index) else: super(ItemDelegate, self).updateEditorGeometry(editor, option, index) class ColorItem(QStandardItem): def __init__(self, color = None): super(ColorItem, self).__init__() if color is not None: self.setData(color) def setData(self, value, role = Qt.EditRole): if role == Qt.EditRole: try: rgb, name = Colors.str2rgb(value) except ValueError as e: print(''Invalid color value: %s'' % e) else: color = QColor(*rgb) # display color icon super(ColorItem, self).setData(color, Qt.DecorationRole) # replace color label value = Colors.colorLabel(color) super(ColorItem, self).setData(value, role) def type(self): return QStandardItem.UserType + 234 class TableView(QTableView): selectColorDialog = pyqtSignal() def __init__(self, model, parent = None): super(TableView, self).__init__(parent) self.setModel(model) self.selectColorDialog.connect(self.colorDialog) def colorDialog(self): index = self.currentIndex() print(''colorDialog:'', index, index.data(Qt.EditRole)) label, color = Colors.updateColor(index.data(Qt.EditRole)) color = QColorDialog.getColor(color, self) if color.isValid(): rgb, name = Colors.lookupColor(color) label = Colors.colorLabel(color) print(label) model = index.model() print(model.data(index)) print(index.row(), index.column()) model.setData(index, label, Qt.EditRole) class Window(QWidget): def __init__(self, parent = None): super(Window, self).__init__(parent) self.setWindowTitle("Multiplex Item Delegate") data = [ # label, value, class, args [ ''intrange'', 0, IntRange, (-10, 10, 1)], [ ''floatrange'', 0.0, FloatRange, (-1.0, 1.0, 0.1, 2)], [ ''choice'', ''one'', Choice, (''one'', ''two'', ''three'', ''four'', ''hundred'')], [ ''bool'', ''false'', Bool, ()], [ ''text'', ''a text'', Text, ()], [ ''color'', ''0,139,139'', Color, ()], [ ''color'', ''255,215,0'', Color, ()], ] # create model, view, and delegate self.model = model = QStandardItemModel(len(data), 2) tableView = TableView(model) delegate = ItemDelegate() tableView.setItemDelegate(delegate) tableView.horizontalHeader().setStretchLastSection(True) if 1: tableView.setEditTriggers( QAbstractItemView.EditKeyPressed | QAbstractItemView.CurrentChanged | QAbstractItemView.DoubleClicked | QAbstractItemView.SelectedClicked) else: tableView.setEditTriggers( QAbstractItemView.EditKeyPressed | QAbstractItemView.DoubleClicked | QAbstractItemView.SelectedClicked) # fill model with data for row, (label, value, cls, args) in enumerate(data): # column 0 model.setItem(row, 0, QStandardItem(label)) # column 1 if cls == Color: item = ColorItem() else: item = QStandardItem() item.setData(value, Qt.EditRole) item.setData(cls(*args), Qt.UserRole) model.setItem(row, 1, item) # finalize window layout = QGridLayout() layout.addWidget(tableView, 0, 0) self.setLayout(layout) self.resize(400, 300) def result(self): model = self.model for row in range(model.rowCount()): label = model.item(row, 0).data(Qt.EditRole) value = model.item(row, 1).data(Qt.EditRole) print(''[%s] %s: %s'' % (row, label, value)) if __name__ == ''__main__'': import sys app = QApplication(sys.argv) window = Window() window.show() ret = app.exec_() window.result() sys.exit(ret)

Cuando se inicia, aparecen varios editores para diferentes tipos en la segunda columna. Los editores de color abren un QComboBox con varios colores predefinidos. El último elemento activa una señal en QTableView, llamando a QColorDialog.getColor ().

Las versiones anteriores intentaban abrir el QColorDialog directamente desde el editor QStyledItemDelegate, similar al código en esta edición , con el mismo resultado (segfault), a diferencia del problema vinculado.

Se podría observar que al abrir el diálogo, se desencadena un cambio en el contexto de la ventana, lo que da como resultado la destrucción del editor delegado de la vista de tabla. Por lo tanto, implementé la señal selectColorDialog, que opera de manera completamente independiente del editor delegado. El backtrace gdb apunta a algunos métodos QAccessible, activados desde QComboBox, pero no puedo ver la relación aquí. Otros intentos para hacer frente a este problema utilizaron QComboBoxes subclasificados con QLineEdits subclase, capturaron e ignoraron los permisos de salida, ocultación y cierre durante el tiempo de vida del diálogo de color, pero fallaron de manera similar.

Program received signal SIGSEGV, Segmentation fault. 0x0000000000cb9270 in ?? () (gdb) bt #0 0x0000000000cb9270 in ?? () #1 0x00007ffff31c5e87 in QAccessible::queryAccessibleInterface(QObject*) () from /usr/lib64/libQt5Gui.so.5 #2 0x00007ffff31c6963 in QAccessibleEvent::accessibleInterface() const () from /usr/lib64/libQt5Gui.so.5 #3 0x00007ffff31c6d9f in QAccessible::updateAccessibility(QAccessibleEvent*) () from /usr/lib64/libQt5Gui.so.5 #4 0x00007fffeebc055a in QComboBoxPrivate::_q_emitCurrentIndexChanged(QModelIndex const&) () from /usr/lib64/libQt5Widgets.so.5 #5 0x00007fffeebc28f5 in QComboBoxPrivate::setCurrentIndex(QModelIndex const&) () from /usr/lib64/libQt5Widgets.so.5 #6 0x00007fffeebc2a56 in QComboBoxPrivate::_q_itemSelected(QModelIndex const&) () from /usr/lib64/libQt5Widgets.so.5 #7 0x00007fffeebc873d in QComboBox::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) () from /usr/lib64/libQt5Widgets.so.5 #8 0x00007ffff5ebb255 in QMetaObject::activate(QObject*, int, int, void**) () from /usr/lib64/libQt5Core.so.5 #9 0x00007fffeebbf61b in QComboBoxPrivateContainer::eventFilter(QObject*, QEvent*) () from /usr/lib64/libQt5Widgets.so.5 #10 0x00007ffff5e93351 in QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject*, QEvent*) () from /usr/lib64/libQt5Core.so.5 #11 0x00007fffeeaca265 in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from /usr/lib64/libQt5Widgets.so.5 #12 0x00007fffeead10fb in QApplication::notify(QObject*, QEvent*) () from /usr/lib64/libQt5Widgets.so.5 #13 0x00007fffef385dee in ?? () from /usr/lib64/python3.4/site-packages/PyQt5/QtWidgets.so #14 0x00007ffff5e93485 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () from /usr/lib64/libQt5Core.so.5 #15 0x00007fffeeb29b4b in QWidgetWindow::event(QEvent*) () from /usr/lib64/libQt5Widgets.so.5 #16 0x00007fffeeaca28c in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from /usr/lib64/libQt5Widgets.so.5 #17 0x00007fffeead11c0 in QApplication::notify(QObject*, QEvent*) () from /usr/lib64/libQt5Widgets.so.5 #18 0x00007fffef385dee in ?? () from /usr/lib64/python3.4/site-packages/PyQt5/QtWidgets.so #19 0x00007ffff5e93485 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () from /usr/lib64/libQt5Core.so.5 #20 0x00007ffff31e9ec0 in QGuiApplicationPrivate::processKeyEvent(QWindowSystemInterfacePrivate::KeyEvent*) ()

¿Alguna idea de alguien, qué está mal aquí? ¡Me encantaría llegar al fondo de esto!

Environment: Python: 3.4.5 Sip: 4.19.2 Qt5: 5.8.0 PyQt5: 5.8.2 Linux: openSUSE/KDE4