PyQt5:动态更改伪类

时间:2019-10-08 15:21:13

标签: python pyqt pyqt5 qtstylesheets

免责声明:我是Qt / PyQt5的相对新手,所以欢迎您提供任何建议。

我正在尝试在PyQt5表单上正确实施验证,以向用户提供适当的反馈。

我当前的测试代码是:

from __future__ import annotations

import typing

from PyQt5.QtCore import QTimer, QRegularExpression
from PyQt5.QtGui import QValidator, QRegularExpressionValidator
from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QVBoxLayout


class MyValidator(QRegularExpressionValidator):
    states = {
        QValidator.Invalid: 'invalid',
        QValidator.Intermediate: 'intermediate',
        QValidator.Acceptable: 'acceptable'
    }

    def validate(self, text: str, pos: int) -> typing.Tuple[QValidator.State, str, int]:
        def set_selector(s):
            p = self.parent()
            p.setProperty('selector', s)
            p.style().unpolish(p)
            p.style().polish(p)
            p.update()

        state, text, pos = super(MyValidator, self).validate(text, pos)
        selector = MyValidator.states[state]

        if selector == 'invalid':
            sel = self.parent().property('selector')
            QTimer.singleShot(1000, lambda: set_selector(sel))
        set_selector(selector)
        return state, text, pos


class Widget(QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)

        self.le = QLineEdit()
        regexp = 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 = QVBoxLayout(self)
        lay.addWidget(self.le)


if __name__ == "__main__":
    import sys

    app = 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_())

我有以下三个“问题”(严格要求恕我直言):

  1. 我本来想使用伪类,但是Qt5不支持AFAIK thy(因此PyQt5也不支持它们)。请注意,@ ekhumoro指出, 支持伪类,但它们是作为硬编码(在c ++中)位图实现的,因此无法向其中添加“自定义伪类”处理当前未实现的事情(例如:验证)。
  2. 为了强制重新评估样式表,我必须为小部件设置一个虚拟样式表,这既丑陋,(更重要的是!)如果使用本地样式表,可能会破坏事情。这与下一个严格联系;请参阅底部的Note2。
  3. 由于只设置QLineEdit被忽略,我被迫重新定义border-color: whatever;边框。同样,这既丑陋又(更重要的是!)是错误的,因为重新创建的边框与原始边框完全不相同。这似乎是由于Qt并未“默认”使用CSS;请参阅底部的Note2。

鉴于上面的问题是:这是实现视觉反馈的正确方法还是还有其他(可能更清洁)的方法?

注意:我理解这个问题有一定数量的基于意见的方面,但是我要求提供统一的“最佳实践”(如果有)。

注2:似乎(欢迎有识之士进行澄清!)Qt默认情况下不使用CSS。这与其他语言/工具包(例如:java / javaFX)不同,在其他语言/工具包中,渲染总是由CSS驱动,并且用户“仅”更新/添加到实际存在的“默认CSS”中。

我的理解(可能是错误的)是边框(或其他任何图形元素)是“默认”“手工绘制”(在c ++中,直接在画布上绘制),而 IFF 用户定义了 relevant CSS元素称为单独的渲染引擎,因此渲染可能会有所不同。

在“边框”的特定情况下,我无法正确定义与“默认”图形相同的CSS边框;如您所见,运行该代码段会在内部默认边框内绘制一个像素;我无法“修复”它。

我对上面的代码感到非常满意,但是任何加深我对QWidget与CSS交互以及相关问题的理解的“答案”都会被接受。

更新:

正如@ekhumoro在评论中指出的,我没有使用Dynamic Properties的Qt批准语法。 关于这两个注意事项:

  1. 使用style().polish()实际上解决了我的垂直边框尺寸问题。
  2. 像在我上面的修订代码中一样,使用style().unpolish() / style().polish()不能 正常工作...部分。对于“静态”边界(IntermediateAcceptable验证程序状态),它实际上可以正常工作,但对于“瞬态”(Invalid)边界则不能很好地工作。

欢迎有识之士。

Update2:

我已按照@ekhumoro(非常感谢!)的建议将代码段更新为最终版本(可以正常工作)。

对我来说,问题已解决。我将开放该问题以备将来参考。

0 个答案:

没有答案