0

我正在尝试使用 aQDataWidgetMapper来驱动自定义QComboBox使用Enum.

我在 Windows 10 上,使用 Python 2.7.13、PySide 1.2.4 (Qt 4.8.7),代码如下:

from PySide.QtCore import Qt, Property
from PySide.QtGui import (
    QApplication,
    QComboBox,
    QDataWidgetMapper,
    QFormLayout,
    QLineEdit,
    QMainWindow,
    QPushButton,
    QStandardItem,
    QStandardItemModel,
    QWidget,
)
from enum import Enum


class State(Enum):

    foo = 1
    bar = 2
    baz = 3


class StateEdit(QComboBox):

    def __init__(self, parent=None):
        super(StateEdit, self).__init__(parent)
        self.addItems(State._member_names_)

    def state(self):
        text = self.currentText()
        return State[text] if text else None

    def setState(self, value):
        if value is None:
            index = -1
        else:
            index = self.findText(value.name, Qt.MatchExactly)
        self.setCurrentIndex(index)

    state = Property(State, state, setState, user=True)


class Window(QMainWindow):

    def __init__(self, parent=None):
        super(Window, self).__init__(parent)

        self._name_edit = QLineEdit()
        self._state_edit = StateEdit()
        self._state_button = QPushButton('Change State')

        self._content = QWidget()
        self.setCentralWidget(self._content)

        self._form = QFormLayout()
        self._content.setLayout(self._form)

        self._form.addRow('Name', self._name_edit)
        self._form.addRow('State', self._state_edit)
        self._form.addRow('Action', self._state_button)

        self._state_button.released.connect(self._on_state_button_clicked)

        self._model = QStandardItemModel()
        name_item = QStandardItem()
        state_item = QStandardItem()
        name_item.setText('My Name')
        state_item.setData(State.bar)
        self._model.appendRow([name_item, state_item])

        self._mapper = QDataWidgetMapper()
        self._mapper.setModel(self._model)
        self._mapper.addMapping(self._name_edit, 0)
        self._mapper.addMapping(self._state_edit, 1, 'state')
        self._mapper.toFirst()

    def _on_state_button_clicked(self):
        self._state_edit.state = State.baz

    def data(self):
        return {
            'name': self._name_edit.text(),
            'state': self._state_edit.state,
        }



if __name__ == "__main__":
    import sys
    from pprint import pprint

    app = QApplication(sys.argv)
    win = Window()
    win.show()
    app.exec_()
    pprint(win.data())

问题是,枚举小部件总是弹出显示它的第一个索引。

属性本身似乎没有问题,因为使用按钮设置它是有效的。

当使用组合框选择不同的索引时,状态也会更新,如窗口关闭时打印的数据所示。

我查看了属性和动态属性,属性上的用户标志,甚至覆盖setPropertyproperty小部件,但无济于事。

我也查看了本指南,但它似乎经常出现问题QComboBox并且QDataWidgetMapper并不真正适用于我的情况。

我看到的唯一解决方案是使用常规工作流程,QComboBox只使用普通的旧索引而不是枚举值,但这太可惜了,我只需要正确触发初始映射,一切都会完美运行。

我真的不知道在哪里看,也许这是一个 PySide 特定的问题,所以任何指针都会有所帮助!

4

2 回答 2

0

因此,为了简单起见,我最终推出了自己的数据映射器。

它没有QDataWidgetMapper公开所有功能,主要是在模型数据更改时更新,但我可以指定我想使用的任何属性,或者一对 getter 和 setter。

如果这可能对某人有帮助,请执行以下操作:

from Qt import QtCore, QtWidgets  # pylint: disable=no-name-in-module


class ModelWidgetMapper(QtCore.QObject):
    """
    :type model: Qt.QtCore.QAbstractItemModel
    :type parent: Qt.QtWidget.QWidget
    """

    class MappingEntry(object):
        """
        :type widget: Qt.QtWidget.QWidget
        :type section: int
        :type property_name: str
        :type getter: callable
        :type setter: callable
        """

        def __init__(self, widget, section, property_name, getter, setter):
            self.widget = widget
            self.section = section
            self.property_name = property_name
            self.getter = getter
            self.setter = setter

    def __init__(self, model, parent=None):
        super(ModelWidgetMapper, self).__init__(parent)
        self._model = model
        self._mapping = []
        self._current_index = None

    def model(self):
        """
        :rtype: Qt.QtCore.QAbstractItemModel
        """
        return self._model

    def add_mapping(self, widget, section, property_name=None, getter=None, setter=None):
        """
        :type widget: Qt.QtWidget.QWidget
        :type section: int
        :type proerty_name: str
        :type getter: callable
        :type setter: callable
        :rtype: ModelWidgetMapper.MappingEntry
        """
        entry = ModelWidgetMapper.MappingEntry(
            widget,
            section,
            property_name,
            getter,
            setter
        )
        self._mapping.append(entry)
        if self._current_index and self._current_index.isValid():
            self._update_widget_from_model(entry)
        return entry

    def submit(self):
        """
        :rtype: bool
        """
        res = True
        for entry in self._mapping:
            res = res & self._update_model_from_widget(entry)
        res & self._model.submit()
        return res

    def revert(self):
        self._model.revert()
        for entry in self._mapping:
            self._update_widget_from_model(entry)

    def set_current_index(self, index):
        """
        :type index: Qt.QtCore.QModelIndex
        """
        self._current_index = index
        for entry in self._mapping:
            self._update_widget_from_model(entry)

    def current_index(self):
        """
        :rtype: Qt.QtCore.QModelIndex
        """
        return self._current_index

    def edits(self):
        """
        :rtype: Dict[int, Any]
        """
        res = {}
        for entry in self._mapping:
            model_value = self._model_value(entry)
            widget_value = self._get_widget_property(entry)
            try:
                widget_value = type(model_value)(widget_value)
            except TypeError:
                pass
            if widget_value != model_value:
                res[entry.section] = widget_value
        return res

    def _model_value(self, entry):
        row = self._current_index.row()
        index = self._model.index(row, entry.section)
        value = self._model.data(index)
        return value

    def _update_widget_from_model(self, entry):
        value = self._model_value(entry)
        self._set_widget_property(entry, value)

    def _update_model_from_widget(self, entry):
        value = self._get_widget_property(entry)
        row = self._current_index.row()
        index = self._model.index(row, entry.section)
        return self._model.setData(index, value)

    def _get_widget_property(self, entry):
        if entry.getter:
            return entry.getter()
        elif not entry.property_name:
            return self._get_widget_user_property(entry.widget)
        return entry.widget.property(entry.property_name)

    def _get_widget_user_property(self, widget):
        return widget.metaObject().userProperty().read(widget)

    def _set_widget_property(self, entry, value):
        if entry.setter:
            return entry.setter(value)
        elif not entry.property_name:
            return self._set_widget_user_property(entry.widget, value)
        return entry.widget.setProperty(entry.property_name, value)

    def _set_widget_user_property(self, widget, value):
        return widget.metaObject().userProperty().write(widget, value)

它使用Qt.py包装器来实现兼容性。

于 2020-01-30T11:04:05.823 回答
0

我测试你的代码。我找到了简单的解决方案。

# At line 68
name_item.setText('My Name')
state_item.setText('')
state_item.setData(State.bar)

实现 setText 方法,然后一切正常。

于 2020-05-02T07:32:51.950 回答