QDataWidgetMapper和验证

时间:2014-05-16 16:16:33

标签: python qt pyside

我有一个包含多个列的树视图。我使用QDataWidgetMapper将每个列与侧栏上的几个小部件之一连接起来。可以通过双击树视图中的单元格或使用侧栏上的小部件来更改数据。

我的一个列包含需要验证的字符串数据。我创建了一个自定义委托,我附加到树视图和数据小部件映射器。它有一个QRegExpValidator,用于防止用户输入时输入无效。此外,在setModelData()中,一旦用户按下"输入",它就会有不同的验证检查。对于树视图,此委托工作正常。对于映射的QLineEdit,有两个问题:

  1. 不会调用QRegExpValidator(可能是因为侧栏小部件没有使用createEditor());因此用户可以键入错误输入到QLineEdit。
  2. 如果数据在setModelData()期间未通过检查,则QLineEdit中的文本不会切换回原始文本。因此,当用户点击QLineEdit以外的内容时,会再次打印错误消息。
  3. 我是以错误的方式解决这个问题吗?

    这是一个简化的例子。为简单起见,我用列表视图替换了树视图:

    class TestWidgetMapperValidate(QtGui.QMainWindow):
        def __init__(self, parent=None):
            super(TestWidgetMapperValidate, self).__init__(parent)
    
            self.centralWidget = QtGui.QWidget()
            self.setCentralWidget(self.centralWidget)
            self.mainLayout = QtGui.QVBoxLayout(self.centralWidget)
    
            # Set up the list view
            self.listView = QtGui.QListView()
            self.listModel = QtGui.QStringListModel(['aaa', 'bbb', 'ccc', 'ddd'])
            self.listView.setModel(self.listModel)
    
            # Set up the delegate
            self.testDelegate = TestDelegate()
            self.listView.setItemDelegateForColumn(0, self.testDelegate)
    
            self.lineEdit = QtGui.QLineEdit()
    
            self.mainLayout.addWidget(self.listView)
            self.mainLayout.addWidget(self.lineEdit)
    
            # Set up the QDataWidgetMapper
            self.mapper = QtGui.QDataWidgetMapper()
            self.mapper.setModel(self.listModel)
            self.mapper.addMapping(self.lineEdit, 0)
            self.mapper.setItemDelegate(self.testDelegate)
    
            self.listView.selectionModel().currentChanged.connect(self.mapper.setCurrentModelIndex)
    
    class TestDelegate(QtGui.QStyledItemDelegate):
        def __init__(self, parent=None):
            super(TestDelegate, self).__init__(parent)
    
        def createEditor(self, parentWidget, option, qModelIndex):
            editor = QtGui.QLineEdit(parentWidget)
            nameRegex = QtCore.QRegExp('[a-zA-Z][a-zA-Z0-9_]+')
            editor.setValidator(QtGui.QRegExpValidator(nameRegex))
            return editor
    
        def setEditorData(self, editor, qModelIndex):
            value = qModelIndex.data(QtCore.Qt.DisplayRole)
            editor.setText(value)
    
        def setModelData(self, editor, model, qModelIndex):
            if not editor.hasAcceptableInput():
                return False
    
            oldValue = qModelIndex.data(QtCore.Qt.DisplayRole)
            newValue = editor.text()
    
            if oldValue != newValue:
                if newValue in model.stringList():
                    print 'That name already exists: {0}'.format(newValue)
                    return False
                else:
                    return model.setData(qModelIndex, newValue)
            else:
                return True
    

    (注意:我使用的是PySide和Python 2.7)

2 个答案:

答案 0 :(得分:1)

这是我想出的。我不知道这是否是最佳解决方案,但它适用于我的情况。

  1. 我最后直接在我的行编辑中添加了一个QRegExpValidator,因为我无法从代理中读取它。以下是__init __()的更新行:

    class TestWidgetMapperValidate(QtGui.QMainWindow):
        def __init__(self, parent=rsui.getMayaMainWindow()):
            # some code omitted here
            self.lineEdit = QtGui.QLineEdit()
            nameRegex = QtCore.QRegExp('[a-zA-Z][a-zA-Z0-9_]+')
            self.lineEdit.setValidator(QtGui.QRegExpValidator(nameRegex))
    
  2. 如果数据未通过委托中的检查,我使用原始值调用setEditorData()以强制它返回旧值。这可以防止错误消息被打印两次。以下是TestDelegate类中更新的setModelData():

    def setModelData(self, editor, model, qModelIndex):
        if not editor.hasAcceptableInput():
            return False
    
        oldValue = qModelIndex.data(QtCore.Qt.DisplayRole)
        newValue = editor.text()
    
        if oldValue != newValue:
            if newValue in model.stringList():
                # The new value is not valid.  Set the data back to the original value.
                self.setEditorData(editor, qModelIndex)
                print 'That name already exists: {0}'.format(newValue)
                return False
            else:
                return model.setData(qModelIndex, newValue)
        else:
            return True
    

答案 1 :(得分:1)

据我所知:

  1. QDataWidgetMapper用于将模型数据映射到现有小部件。因此,正如您所提到的,永远不会调用委托方法createEditor。换句话说:QDataWidgetMapper使用委托方法createEditor和视图一起使用代替。另一种方法是为侧边栏创建一个视图,用它注册您的委托(以便视图调用createEditor)并共享tableView的选择机制。见http://qt-project.org/doc/qt-5/model-view-programming.html#sharing-selections-among-views
  2. 如果您要按照上面的建议创建侧边栏视图,则可以通过恢复编辑器值来处理setModelData返回false的事件。
  3. 我必须承认,我对模型视图相对较新。我有兴趣听到其他人的意见。这是一个很好的问题!