【问题标题】:Get visual feedback from QValidator从 QValidator 获得视觉反馈
【发布时间】:2020-02-05 06:39:59
【问题描述】:

我正在尝试使用QValidator 后代(实际上在 PyQt5 中,但这不重要)来验证一系列行编辑。

一小段摘录是:

class IPv4(QWidget):
    def __init__(self):
        super(IPv4, self).__init__()
        uic.loadUi('ipv4.ui', self)
        self.address.inputMask = ''
        rx = QRegularExpression(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}')
        self.address.setValidator(QRegularExpressionValidator(rx, self.address))
        self.netmask.setValidator(QRegularExpressionValidator(rx, self.netmask))
        self.gateway.setValidator(QRegularExpressionValidator(rx, self.gateway))
        self.broadcast.setValidator(QRegularExpressionValidator(rx, self.broadcast))
        self.dns1.setValidator(QRegularExpressionValidator(rx, self.dns1))
        self.dns2.setValidator(QRegularExpressionValidator(rx, self.dns2))
        self.on_dhcp_clicked(self.dhcp.isChecked())

这与宣传的一样有效,但用户没有得到任何反馈,因为尝试输入“错误”字符只会丢弃它们。

除了连接到QLineEdit.textChanged 信号并“手动”进行验证(即:设置验证器,否则出现错误text 不会' t 变化,不会发出任何信号)。首选的反馈是更改行编辑的边框颜色。

这在某种程度上违背了验证器本身的目的。似乎我遗漏了一些东西,因为我看不到如何触发来自QValidator 的反馈。

处理这个问题的“标准方法”是什么?

【问题讨论】:

    标签: python qt pyqt pyqt5 qvalidator


    【解决方案1】:

    通过在子类中重新实现validate 方法,可以使用自定义信号来指示验证状态更改。下面是演示此方法的脚本。 (请注意,validate 的签名在 PyQt 中是不同的,因为它不会像在 C++ 中那样改变参数)。

    import sys
    from PyQt5 import QtCore, QtGui, QtWidgets
    
    class RegExpValidator(QtGui.QRegularExpressionValidator):
        validationChanged = QtCore.pyqtSignal(QtGui.QValidator.State)
    
        def validate(self, input, pos):
            state, input, pos = super().validate(input, pos)
            self.validationChanged.emit(state)
            return state, input, pos
    
    class Window(QtWidgets.QWidget):
        def __init__(self):
            super().__init__()
            regexp = QtCore.QRegularExpression(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}')
            validator = RegExpValidator(regexp, self)
            validator.validationChanged.connect(self.handleValidationChange)
            self.edit = QtWidgets.QLineEdit()
            self.edit.setValidator(validator)
            layout = QtWidgets.QVBoxLayout(self)
            layout.addWidget(self.edit)
    
        def handleValidationChange(self, state):
            if state == QtGui.QValidator.Invalid:
                colour = 'red'
            elif state == QtGui.QValidator.Intermediate:
                colour = 'gold'
            elif state == QtGui.QValidator.Acceptable:
                colour = 'lime'
            self.edit.setStyleSheet('border: 3px solid %s' % colour)
            QtCore.QTimer.singleShot(1000, lambda: self.edit.setStyleSheet(''))
    
    
    if __name__ == "__main__":
    
        app = QtWidgets.QApplication(sys.argv)
        window = Window()
        window.show()
        sys.exit(app.exec_())
    

    【讨论】:

      【解决方案2】:

      如果要验证 QLineEdit 文本是否有效,则必须使用 hasAcceptableInput() 方法:

      from PyQt5 import QtCore, QtGui, QtWidgets
      
      
      class Widget(QtWidgets.QWidget):
          def __init__(self, parent=None):
              super(Widget, self).__init__(parent)
      
              rx = QtCore.QRegularExpression(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}")
      
              self.le = QtWidgets.QLineEdit()
              self.le.setValidator(QtGui.QRegularExpressionValidator(rx, self.le))
              self.le.textChanged.connect(self.on_textChanged)
      
              lay = QtWidgets.QVBoxLayout(self)
              lay.addWidget(self.le)
      
          @QtCore.pyqtSlot()
          def on_textChanged(self):
              le = self.sender()
              if isinstance(le, QtWidgets.QLineEdit):
                  le.setStyleSheet(
                      "border: 5px solid {color}".format(
                          color="green" if le.hasAcceptableInput() else "red"
                      )
                  )
      
      
      if __name__ == "__main__":
          import sys
      
          app = QtWidgets.QApplication(sys.argv)
      
          w = Widget()
          w.show()
      
          sys.exit(app.exec_())
      

      【讨论】:

      • 正如 OP 中所解释的,这 not 确实有效。在正常操作中QLineEdit.validator 优先,因此on_textChanged() 在无效输入上根本不会被调用。您的代码实际上区分了IntermediateAcceptable。对于真正的Invalid 条目,仍然没有反馈(甚至没有哔声)。附带问题:您为什么要编辑标签?我认为这不是 pyqt 特有的;它看起来像是原始 QValidator 类中的设计问题。
      【解决方案3】:

      我接受@ekhumoro 的回答基本正确,但我也会发布我当前的测试代码(恕我直言!)从长远来看更容易维护。

      from __future__ import annotations
      
      import typing
      
      from PyQt5 import QtCore, QtGui, QtWidgets
      
      
      class MyValidator(QtGui.QRegularExpressionValidator):
          def validate(self, text: str, pos: int) -> typing.Tuple[QtGui.QValidator.State, str, int]:
              state, text, pos = super(MyValidator, self).validate(text, pos)
              selector = {
                  QtGui.QValidator.Invalid: 'invalid',
                  QtGui.QValidator.Intermediate: 'intermediate',
                  QtGui.QValidator.Acceptable: 'acceptable'
              }[state]
      
              if selector == 'invalid':
                  sel = self.parent().property('selector')
      
                  def restore():
                      self.parent().setProperty('selector', sel)
                      self.parent().setStyleSheet('/**/')
                  QtCore.QTimer.singleShot(1000,  restore)
              self.parent().setProperty('selector', selector)
              self.parent().setStyleSheet('/**/')
              return state, text, pos
      
      
      class Widget(QtWidgets.QWidget):
          def __init__(self, parent=None):
              super(Widget, self).__init__(parent)
      
              self.le = QtWidgets.QLineEdit()
              regexp = QtCore.QRegularExpression(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}')
              self.le.setValidator(MyValidator(regexp, self.le))
              self.le.setProperty('selector', 'none')
      
              lay = QtWidgets.QVBoxLayout(self)
              lay.addWidget(self.le)
      
      
      if __name__ == "__main__":
          import sys
      
          app = QtWidgets.QApplication(sys.argv)
          app.setStyleSheet('''\
          *[selector="invalid"] {border-radius: 3px; border: 1px solid red;}
          *[selector="intermediate"] {border-radius: 3px; border: 1px solid gold;}
          *[selector="acceptable"] {border-radius: 3px; border: 1px solid green;}
          ''')
      
          w = Widget()
          w.show()
      
          sys.exit(app.exec_())
      

      这段代码有两个(挑剔)问题;但这是另一个问题的问题;)

      【讨论】:

        猜你喜欢
        • 2015-10-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-20
        • 1970-01-01
        • 2017-07-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多