为什么`QtGui.QValidator.validate`的返回如此不一致?处理这个问题的稳健方法?

时间:2014-11-05 14:28:11

标签: python pyqt pyqt4 pyside pyqt5

我正在尝试开发一个使用Qt的应用。我想让应用程序同时使用PySidePyQt4。除了我的自定义QtGui.QValidator类之外,我没有遇到任何重大的兼容性问题:

import sys
import os
import re

variant = 'PyQt4'
#variant = 'PySide'

print 'Python {}, {}'.format(sys.version, sys.platform)

# Using PyQt4:
if variant == 'PySide':
    import PySide
    from PySide import QtGui, QtCore
    print 'PySide {}, Qt {}'.format(PySide.__version__, PySide.QtCore.__version__)

# Using PySide:
if variant == 'PyQt4':
    from PyQt4 import QtGui, QtCore
    from PyQt4.Qt import PYQT_VERSION_STR
    print 'PyQt4 {}, Qt {}'.format(PYQT_VERSION_STR, QtCore.QT_VERSION_STR)

class ExpressionValidator(QtGui.QValidator):
    def __init__(self, parent=None,):
        QtGui.QValidator.__init__(self, parent)

        self.states = {'invalid':      QtGui.QValidator.Invalid,
                      'intermediate':  QtGui.QValidator.Intermediate,
                      'acceptable':    QtGui.QValidator.Acceptable,
                      }

        self.regX_expression = re.compile('([0-9.eEpiPI+-/*\(\))\^]*)')

    def returnState(self, state, text, pos):

        if state == 'acceptable':
            color = '#00cc00' # green
        elif state == 'intermediate':
            color = '#fff79a' # yellow
        else:
            color = '#f6989d' # red
        self.parent().setStyleSheet('QLineEdit {{ background-color: {} }}'.format(color))

        if variant == 'PySide':
            return self.states[state]
        else:
            return (self.states[state], pos)


    def validate(self, textInput, pos):
        # Check text, return state
        matches = self.regX_expression.findall(textInput)
        if matches and len(matches[0]) == len(textInput):
            if len(textInput) >0 and str(textInput)[-1] in '+-/*^':
                self.parent().setToolTip('Expression Needs Finished.')
                return self.returnState('intermediate', textInput, pos)
            else:
                return self.returnState('acceptable', textInput, pos)
        else:
            return self.returnState('invalid', textInput, pos)


if __name__ == "__main__":

    app = QtGui.QApplication(sys.argv)
    app.setStyle('GTK')
    mainWindow = QtGui.QMainWindow()

    lineEdit = QtGui.QLineEdit()
    lineEdit.setValidator(ExpressionValidator(lineEdit))

    mainWindow.setCentralWidget(lineEdit)
    mainWindow.show()

    app.exec_()

    app.deleteLater()
    sys.exit()

我需要在returnState函数中放置健壮的代码。我是否只需要在多个系统上测试它并继续构建我的if语句?

到目前为止,这些调用都有效:

  • Python 2.7.6,Windows 7,PyQt4 4.10.4,Qt 4.8.6:

    • return (self.states[state], pos)
  • Python 2.7.6,Windows 7,PySide 1.2.1,Qt 4.8.6:

    • return self.states[state]

    • return (self.states[state], text, pos)

  • Python 2.7.8,OpenSuse(linux2),PyQt4 4.10.4,Qt 4.8.5:

    • return (self.states[state], pos)
  • Python 2.7,OpenSuse(linux2),PyQt4 4.8.3,Qt 4.7.1:

    • return (self.states[state], pos)
  • Python 2.7.8,OpenSuse(linux2),PySide 1.2.1,Qt 4.8.5:

    • return self.states[state]

    • return (self.states[state], text, pos)

如何为正确的呼叫解析help(QtGui.QValidator.validate)并基于此动态创建回复呼叫呢?

1 个答案:

答案 0 :(得分:2)

<强>更新

正如Onlyjus在评论中指出的那样,确切的行为因使用which PyQt api而异。

使用v1 api,input参数将是QString,这意味着它可以就地修改(这就是C ++ api的工作方式)。但是对于v2 api,input参数是一个不可变的python字符串,因此必须返回它。所以实际的签名是:

# v1 api (default for Python 2)
QValidator.validate(QString, int) -> (QValidator.State, int)
# v2 api (default for Python 3)
QValidator.validate(str, int) -> (QValidator.State, str, int)

这些(和许多其他)差异记录在PyQt4文档的PyQt4 and Python v3部分。

PySide的行为虽然有点奇怪,但至少是可以预测的。它将允许返回所有任意长度的元组,只要前三个值的类型与C ++签名相符。所以这些都可行:

    return (QValidator.State,)
    return (QValidator.State, "string")
    return (QValidator.State, "string", 10)
    return (QValidator.State, "string", 10, "foo", 60, "blah")

但是这种灵活性在当前情况下都没有帮助,因为在使用v1 api时,PyQt4无法匹配任何这些变体。

看起来唯一真正的解决方案是切换到v2 api(至少为QString)。

但是,如果你不能这样做,那么最好的妥协就是使用与你正在做的非常相似的东西:

    if variant == 'PyQt4':
        # if necessary, modify text in place here
        # rather than returning it
        return (self.states[state], pos)
    else:
        return (self.states[state], text, pos)

这应该适用于PySide,PyQt4以及PyQt5(如果你曾选择为它提供支持)。