如何修复由样式表修改的核心控件中损坏的 sizeHint?

发布于 2025-01-20 02:34:26 字数 4828 浏览 0 评论 0 原文

我有一个应用程序 qabstractspinbox 使用范围范围的样式表的应用程序,如下所示:

QAbstractSpinBox {
    border: 2px inset grey;
    text-align: right;
    padding-left: 1px;
    padding-right: 1px;
    qproperty-buttonSymbols: PlusMinus;
}

QAbstractSpinBox::up-button
{
    subcontrol-origin: margin;
    subcontrol-position: right;
    width: 25px;
    height: 21px;
    right: 1px;
}

QAbstractSpinBox::down-button
{
    subcontrol-origin: margin;
    subcontrol-position: left;
    width: 25px;
    height: 21px;
    left: 2px;
}

此处的目的是在侧面获得带有+/-按钮的旋转箱,例如:

看起来还可以,但控件在布局中的行为不正确;特别是, sizeHint()是错误的,如 qdoublespinbox

png“ rel =” nofollow noreferrer“> 派生的类修复 sizehint() 的类别,所以我去了问题的底部并检查了 qabstractspinbox 实现其 sizeHint()的实现,因此请查找提示:

QSize QAbstractSpinBox::sizeHint() const
{
    Q_D(const QAbstractSpinBox);
    if (d->cachedSizeHint.isEmpty()) {
        ensurePolished();

        const QFontMetrics fm(fontMetrics());
        int h = d->edit->sizeHint().height();
        int w = 0;
        QString s;
        QString fixedContent =  d->prefix + d->suffix + QLatin1Char(' ');
        s = d->textFromValue(d->minimum);
        s.truncate(18);
        s += fixedContent;
        w = qMax(w, fm.horizontalAdvance(s));
        s = d->textFromValue(d->maximum);
        s.truncate(18);
        s += fixedContent;
        w = qMax(w, fm.horizontalAdvance(s));

        if (d->specialValueText.size()) {
            s = d->specialValueText;
            w = qMax(w, fm.horizontalAdvance(s));
        }
        w += 2; // cursor blinking space

        QStyleOptionSpinBox opt;
        initStyleOption(&opt);
        QSize hint(w, h);
        d->cachedSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
                            .expandedTo(QApplication::globalStrut());
    }
    return d->cachedSizeHint;
}

考虑到内容可以假设的最大宽度(使用Max/Max/使用max/the Max/最小/特殊文本),然后将整个尺寸计算为 QStyle :: SizeFromContents 从当前样式计算。反过来, qStylesHeetStyle :: SizeFromContents 中的相关部分是:

    case CT_SpinBox:
        if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
            if (spinbox->buttonSymbols != QAbstractSpinBox::NoButtons) {
                // Add some space for the up/down buttons
                QRenderRule subRule = renderRule(w, opt, PseudoElement_SpinBoxUpButton);
                if (subRule.hasDrawable()) {
                    QRect r = positionRect(w, rule, subRule, PseudoElement_SpinBoxUpButton,
                                           opt->rect, opt->direction);
                    sz.rwidth() += r.width();
                } else {
                    QSize defaultUpSize = defaultSize(w, subRule.size(), spinbox->rect, PseudoElement_SpinBoxUpButton);
                    sz.rwidth() += defaultUpSize.width();
                }
            }
            if (rule.hasBox() || rule.hasBorder() || !rule.hasNativeBorder())
                sz = rule.boxSize(sz);
            return sz;
        }
        break;

不介绍详细信息,很明显,只有UP按钮才被考虑,并且仅用于其宽度,并且有两个基本假设。按钮垂直堆叠;向下按钮被完全忽略。

我试图通过自定义 qStyle 来解决问题:

struct GLProxyStyle : QProxyStyle {
    virtual QSize sizeFromContents(ContentsType ct, const QStyleOption *opt, const QSize &contentsSize, const QWidget *w) const override {
        QSize ret = QProxyStyle::sizeFromContents(ct, opt, contentsSize, w);
        if (ct == CT_SpinBox) {
            if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
                if (spinbox->buttonSymbols != QAbstractSpinBox::NoButtons) {
                    QRect downRect = subControlRect(CC_SpinBox, spinbox, SC_SpinBoxDown, w);
                    QRect upRect = subControlRect(CC_SpinBox, spinbox, SC_SpinBoxDown, w);
                    if (downRect.left() != upRect.left()) {
                        ret.rwidth() += downRect.width();
                    }
                }
            }
        }
        return ret;
    }
};

但是不幸的是,设置样式表使其覆盖 qstyle

目前,我要根据调整的大小构建所有小部件的调整尺寸提示,强迫最小尺寸()全球。

因此,提出我的问题:

  • 有没有办法覆盖 QStyleSheetStyle size> sizefromcontents 的实现?
  • 或者,总的来说:还有其他方法可以解决我错过的问题吗?

I have an application that styles QAbstractSpinBox using an application-wide stylesheet, that goes like this:

QAbstractSpinBox {
    border: 2px inset grey;
    text-align: right;
    padding-left: 1px;
    padding-right: 1px;
    qproperty-buttonSymbols: PlusMinus;
}

QAbstractSpinBox::up-button
{
    subcontrol-origin: margin;
    subcontrol-position: right;
    width: 25px;
    height: 21px;
    right: 1px;
}

QAbstractSpinBox::down-button
{
    subcontrol-origin: margin;
    subcontrol-position: left;
    width: 25px;
    height: 21px;
    left: 2px;
}

The objective here is to obtain spinboxes with +/- buttons on the sides, like this:

example of a QSpinBox with +/- buttons on the sides

While the look is ok, the control does not behave correctly in layouts; in particular, the sizeHint() is wrong, as can be seen here for the QDoubleSpinBox:

two QSpinBox and two QDoubleSpinBox, where QDoubleSpinBoxes have clearly wrong sizeHint

Experimenting a bit, it turned out that it's not taking into account the size needed for one of the buttons; I don't want to replace each and every QAbstractSpinBox derived classes with my own subclassed version that fixes the sizeHint(), so I went at the bottom of the issue and checked how QAbstractSpinBox implements its sizeHint() so look for hints:

QSize QAbstractSpinBox::sizeHint() const
{
    Q_D(const QAbstractSpinBox);
    if (d->cachedSizeHint.isEmpty()) {
        ensurePolished();

        const QFontMetrics fm(fontMetrics());
        int h = d->edit->sizeHint().height();
        int w = 0;
        QString s;
        QString fixedContent =  d->prefix + d->suffix + QLatin1Char(' ');
        s = d->textFromValue(d->minimum);
        s.truncate(18);
        s += fixedContent;
        w = qMax(w, fm.horizontalAdvance(s));
        s = d->textFromValue(d->maximum);
        s.truncate(18);
        s += fixedContent;
        w = qMax(w, fm.horizontalAdvance(s));

        if (d->specialValueText.size()) {
            s = d->specialValueText;
            w = qMax(w, fm.horizontalAdvance(s));
        }
        w += 2; // cursor blinking space

        QStyleOptionSpinBox opt;
        initStyleOption(&opt);
        QSize hint(w, h);
        d->cachedSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
                            .expandedTo(QApplication::globalStrut());
    }
    return d->cachedSizeHint;
}

Which essentially calculates a size for the internal textbox part considering the maximum width that the content can assume (using the max/min/special text), and then delegates calculating the full size to QStyle::sizeFromContents from the current style. In turn, the relevant portion in QStyleSheetStyle::sizeFromContents is this:

    case CT_SpinBox:
        if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
            if (spinbox->buttonSymbols != QAbstractSpinBox::NoButtons) {
                // Add some space for the up/down buttons
                QRenderRule subRule = renderRule(w, opt, PseudoElement_SpinBoxUpButton);
                if (subRule.hasDrawable()) {
                    QRect r = positionRect(w, rule, subRule, PseudoElement_SpinBoxUpButton,
                                           opt->rect, opt->direction);
                    sz.rwidth() += r.width();
                } else {
                    QSize defaultUpSize = defaultSize(w, subRule.size(), spinbox->rect, PseudoElement_SpinBoxUpButton);
                    sz.rwidth() += defaultUpSize.width();
                }
            }
            if (rule.hasBox() || rule.hasBorder() || !rule.hasNativeBorder())
                sz = rule.boxSize(sz);
            return sz;
        }
        break;

Without going into the details, it's clear that only the Up button is considered, and only for its width, with the underlying assumption that the two buttons are stacked vertically; the down button is ignored completely.

I tried to work around the problem by means of a custom QStyle:

struct GLProxyStyle : QProxyStyle {
    virtual QSize sizeFromContents(ContentsType ct, const QStyleOption *opt, const QSize &contentsSize, const QWidget *w) const override {
        QSize ret = QProxyStyle::sizeFromContents(ct, opt, contentsSize, w);
        if (ct == CT_SpinBox) {
            if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
                if (spinbox->buttonSymbols != QAbstractSpinBox::NoButtons) {
                    QRect downRect = subControlRect(CC_SpinBox, spinbox, SC_SpinBoxDown, w);
                    QRect upRect = subControlRect(CC_SpinBox, spinbox, SC_SpinBoxDown, w);
                    if (downRect.left() != upRect.left()) {
                        ret.rwidth() += downRect.width();
                    }
                }
            }
        }
        return ret;
    }
};

but unfortunately setting a stylesheet makes it override QStyle.

For now I'm forcing a minimum size depending on an adjusted size hint after building all widgets, but that's a pain and, most importantly, it introduces a relayout, so I'd like to find a way to fix the sizeHint() globally.

So, coming to my question:

  • is there a way to override the QStyleSheetStyle implementation of sizeFromContents?
  • or, more in general: is there some other way to fix this that I missed?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文