QCheckbox/QRadioButton 换行

发布于 2024-08-12 21:42:33 字数 302 浏览 5 评论 0原文

我正在尝试使用标准 QCheckbox/QRadioButton 与 Qt 建立多行复选框/单选按钮。

我没有找到直接的解决方案,因为 QRadioButton{wrap:true;} 没有效果。 唯一可能的就是访问 QRadioButton->label->setLineWrap(true) 但

  1. 我想从设计者那里做到这一点,
  2. 而不必重写小部件

除了放置之外还有什么想法QRadioButton 和 QLabel 彼此相邻?

谢谢,鲍里斯。

I'm trying to have a multi-line checkbox/radiobutton with Qt using standard QCheckbox/QRadioButton.

I didn't find the direct solution since QRadioButton{wrap:true;} has no effect.
The only thing possible would be to access to the QRadioButton->label->setLineWrap(true) but

  1. I'd like to do that from the designer
  2. not having to rewrite a widget

Any idea beside putting a QRadioButton and a QLabel next to each others?

Thx, Boris.

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

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

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

胡大本事 2024-08-19 21:42:34

我的解决方案:

#ifndef CHECKBOX_H
#define CHECKBOX_H

#include <QCheckBox>

#include <QHBoxLayout>
#include <QLabel>

class CheckBox : public QCheckBox
{
    Q_OBJECT
public:
   explicit CheckBox(QWidget *parent = 0);

   void setText(const QString & text);
   QSize sizeHint() const;
   bool hitButton(const QPoint &pos) const;

protected:
   void paintEvent(QPaintEvent *);

private:
   QHBoxLayout* _layout;
   QLabel*      _label;
};

#endif // CHECKBOX_H




#include "checkbox.h"

#include <QStylePainter>
#include <QStyleOption>

#define MARGIN 4 // hardcoded spacing acording to QCommonStyle implementation

CheckBox::CheckBox(QWidget *parent) : QCheckBox(parent)
{
    setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred, QSizePolicy::CheckBox));

    QStyleOptionButton opt;
    initStyleOption(&opt);

    QRect label_rect = style()->subElementRect(QStyle::SE_CheckBoxContents, &opt, this);

    _label = new QLabel(this);
    _label->setWordWrap(true);
    _label->setMouseTracking(true);
    //_label->setMinimumHeight(label_rect.height());

    _layout = new QHBoxLayout(this);
    _layout->setContentsMargins(label_rect.left()+MARGIN, MARGIN/2, MARGIN/2, MARGIN/2);
    _layout->setSpacing(0);
    _layout->addWidget(_label);

    setLayout(_layout);
}

void CheckBox::setText(const QString & text)
{
    _label->setText(text);
    QCheckBox::setText(text);
}

void CheckBox::paintEvent(QPaintEvent *)
{
    QStylePainter p(this);
    QStyleOptionButton opt;
    initStyleOption(&opt);

    QStyleOptionButton subopt = opt;
    subopt.rect = style()->subElementRect(QStyle::SE_CheckBoxIndicator, &opt, this);
    subopt.rect.moveTop(opt.rect.top()+MARGIN/2); // align indicator to top

    style()->proxy()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &subopt, &p, this);

    if (opt.state & QStyle::State_HasFocus)
    {
        QStyleOptionFocusRect fropt;
        fropt.QStyleOption::operator=(opt);
        fropt.rect = style()->subElementRect(QStyle::SE_CheckBoxFocusRect, &opt, this);
        style()->proxy()->drawPrimitive(QStyle::PE_FrameFocusRect, &fropt, &p, this);
    }
}

QSize CheckBox::sizeHint() const
{
    return QSize(); // will be calculated by layout
}

bool CheckBox::hitButton(const QPoint &pos) const
{
    QStyleOptionButton opt;
    initStyleOption(&opt);
    return opt.rect.contains(pos); // hit all button
}

My solution:

#ifndef CHECKBOX_H
#define CHECKBOX_H

#include <QCheckBox>

#include <QHBoxLayout>
#include <QLabel>

class CheckBox : public QCheckBox
{
    Q_OBJECT
public:
   explicit CheckBox(QWidget *parent = 0);

   void setText(const QString & text);
   QSize sizeHint() const;
   bool hitButton(const QPoint &pos) const;

protected:
   void paintEvent(QPaintEvent *);

private:
   QHBoxLayout* _layout;
   QLabel*      _label;
};

#endif // CHECKBOX_H




#include "checkbox.h"

#include <QStylePainter>
#include <QStyleOption>

#define MARGIN 4 // hardcoded spacing acording to QCommonStyle implementation

CheckBox::CheckBox(QWidget *parent) : QCheckBox(parent)
{
    setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred, QSizePolicy::CheckBox));

    QStyleOptionButton opt;
    initStyleOption(&opt);

    QRect label_rect = style()->subElementRect(QStyle::SE_CheckBoxContents, &opt, this);

    _label = new QLabel(this);
    _label->setWordWrap(true);
    _label->setMouseTracking(true);
    //_label->setMinimumHeight(label_rect.height());

    _layout = new QHBoxLayout(this);
    _layout->setContentsMargins(label_rect.left()+MARGIN, MARGIN/2, MARGIN/2, MARGIN/2);
    _layout->setSpacing(0);
    _layout->addWidget(_label);

    setLayout(_layout);
}

void CheckBox::setText(const QString & text)
{
    _label->setText(text);
    QCheckBox::setText(text);
}

void CheckBox::paintEvent(QPaintEvent *)
{
    QStylePainter p(this);
    QStyleOptionButton opt;
    initStyleOption(&opt);

    QStyleOptionButton subopt = opt;
    subopt.rect = style()->subElementRect(QStyle::SE_CheckBoxIndicator, &opt, this);
    subopt.rect.moveTop(opt.rect.top()+MARGIN/2); // align indicator to top

    style()->proxy()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &subopt, &p, this);

    if (opt.state & QStyle::State_HasFocus)
    {
        QStyleOptionFocusRect fropt;
        fropt.QStyleOption::operator=(opt);
        fropt.rect = style()->subElementRect(QStyle::SE_CheckBoxFocusRect, &opt, this);
        style()->proxy()->drawPrimitive(QStyle::PE_FrameFocusRect, &fropt, &p, this);
    }
}

QSize CheckBox::sizeHint() const
{
    return QSize(); // will be calculated by layout
}

bool CheckBox::hitButton(const QPoint &pos) const
{
    QStyleOptionButton opt;
    initStyleOption(&opt);
    return opt.rect.contains(pos); // hit all button
}
贩梦商人 2024-08-19 21:42:33

这确实很烦人,如果不重新实现就无法解决:如果我没记错的话,标签使用 Qt::PlainText 。在我们的项目中,UI 团队通过两种方法解决了这个问题。

使用 QRadioButton 按钮作为标题

使用 QRadioButton 作为标题的布局

以仅包含该选项的简短描述的方式重写 QRadioButton 文本。在其下方放置一个 QLabel 并添加更长的描述。我们使用较小的字体和一点缩进,使其看起来整洁。例如,这在 Mac OS X 中经常使用。

从单选按钮中删除文本

使用伙伴标签的布局

重新布局 UI,以便将每个单选按钮放置在 QLabel 的左侧。将整个文本添加到 QLabel 并将标签设置为单选按钮的伙伴。这是功能最少的方法,因为单击标签不会选中单选按钮。另外,对齐也不是很好。

That indeed is really annoying and cannot be solved without reimplementation: the label uses Qt::PlainText if I'm not mistaken. In our projects, the UI team solved this with two approaches.

Using the QRadioButton button as a title

Layout using the QRadioButton as a title

Rewrite the QRadioButton text in such a fashion that it has only a brief description of the option. Put a QLabel underneath it and add a longer description. We use a smaller font and a little indentation to make it look neat. This is used frequently in Mac OS X, for example.

Removing the text from the radio button

Layout using buddy labels

Relayout the UI so that each radio button is put on the left of a QLabel. Add the whole text to the QLabel and set the label as the radio buttons' buddy. This is the least functional approach, because clicking the label does not check the radio button. Also, alignment is not very good.

ゞ花落谁相伴 2024-08-19 21:42:33

我知道的唯一方法(但它不是“可布局的”)是将 \n 符号放入字符串中。

The only way I know (but it's not "layoutable") is putting \n sign into a string.

擦肩而过的背影 2024-08-19 21:42:33

问题

不幸的是,对此没有简短的解决方案。有一个关于此的 Qt 功能请求(请参阅 QTBUG-5370),但此后一直处于待处理状态2009 年(!),所以它可能永远不会实现。

然而,实际上可以通过重写 QRadioButton::resizeEvent 并在那里执行必要的换行来实现这一点。除此之外,我们还必须调整水平尺寸策略并覆盖最小尺寸提示,以便单选按钮的布局正确。

下面给出了完整的实现。

LineWrappedRadioButton.h

#include <QRadioButton>

class LineWrappedRadioButton : public QRadioButton {
    Q_OBJECT
private:
    void wrapLines(int width);
protected:
    virtual void resizeEvent(QResizeEvent *event);
public:
    LineWrappedRadioButton(QWidget *parent = nullptr) : LineWrappedRadioButton(QString(), parent) { }
    LineWrappedRadioButton(const QString &text, QWidget *parent = nullptr);
    virtual QSize minimumSizeHint() const { return QSize(QRadioButton().minimumSizeHint().width(), sizeHint().height()); }
};

LineWrappedRadioButton.cpp

#include "LineWrappedRadioButton.h"
#include <QRadioButton>
#include <QResizeEvent>
#include <QStyle>

void LineWrappedRadioButton::wrapLines(int width) {
    QString word, line, result;
    for (QChar c : text().replace('\n', ' ') + ' ') {
        word += c;
        if (c.isSpace()) {
            if (!line.isEmpty() && fontMetrics().width(line + word.trimmed()) > width) {
                result += line.trimmed() + '\n';
                line = word;
            } else {
                line += word;
            }
            word.clear();
        }
    }
    result += line.trimmed();
    setText(result.trimmed());
}

void LineWrappedRadioButton::resizeEvent(QResizeEvent *event) {
    int controlElementWidth = sizeHint().width() - style()->itemTextRect(fontMetrics(), QRect(), Qt::TextShowMnemonic, false, text()).width();
    wrapLines(event->size().width() - controlElementWidth);
    QRadioButton::resizeEvent(event);
}

LineWrappedRadioButton::LineWrappedRadioButton(const QString &text, QWidget *parent) : QRadioButton(text, parent) {
    QSizePolicy policy = sizePolicy();
    policy.setHorizontalPolicy(QSizePolicy::Preferred);
    setSizePolicy(policy);
    updateGeometry();
}

main.cpp

#include "LineWrappedRadioButton.h"
#include <QApplication>

int main(int argc, char **argv) {
    QApplication app(argc, argv);
    LineWrappedRadioButton button("Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua.");
    button.show();
    return app.exec();
}

这将创建一个可自由调整大小的窗口,其中包含一个换行单选按钮:

LineWrappedRadioButton 屏幕截图

The problem

There unfortunately is no short and simple solution for this. There's a Qt feature request about this (see QTBUG-5370), but it's pending since 2009 (!), so it probably will never be implemented.

However, it's actually possible to implement this by overriding the QRadioButton::resizeEvent and to perform the necessary line wrapping there. In addition to that, we must also adjust the horizontal size policy and override the minimum size hint so that the layout of the radio button will be correct.

The complete implementation is given below.

LineWrappedRadioButton.h

#include <QRadioButton>

class LineWrappedRadioButton : public QRadioButton {
    Q_OBJECT
private:
    void wrapLines(int width);
protected:
    virtual void resizeEvent(QResizeEvent *event);
public:
    LineWrappedRadioButton(QWidget *parent = nullptr) : LineWrappedRadioButton(QString(), parent) { }
    LineWrappedRadioButton(const QString &text, QWidget *parent = nullptr);
    virtual QSize minimumSizeHint() const { return QSize(QRadioButton().minimumSizeHint().width(), sizeHint().height()); }
};

LineWrappedRadioButton.cpp

#include "LineWrappedRadioButton.h"
#include <QRadioButton>
#include <QResizeEvent>
#include <QStyle>

void LineWrappedRadioButton::wrapLines(int width) {
    QString word, line, result;
    for (QChar c : text().replace('\n', ' ') + ' ') {
        word += c;
        if (c.isSpace()) {
            if (!line.isEmpty() && fontMetrics().width(line + word.trimmed()) > width) {
                result += line.trimmed() + '\n';
                line = word;
            } else {
                line += word;
            }
            word.clear();
        }
    }
    result += line.trimmed();
    setText(result.trimmed());
}

void LineWrappedRadioButton::resizeEvent(QResizeEvent *event) {
    int controlElementWidth = sizeHint().width() - style()->itemTextRect(fontMetrics(), QRect(), Qt::TextShowMnemonic, false, text()).width();
    wrapLines(event->size().width() - controlElementWidth);
    QRadioButton::resizeEvent(event);
}

LineWrappedRadioButton::LineWrappedRadioButton(const QString &text, QWidget *parent) : QRadioButton(text, parent) {
    QSizePolicy policy = sizePolicy();
    policy.setHorizontalPolicy(QSizePolicy::Preferred);
    setSizePolicy(policy);
    updateGeometry();
}

main.cpp

#include "LineWrappedRadioButton.h"
#include <QApplication>

int main(int argc, char **argv) {
    QApplication app(argc, argv);
    LineWrappedRadioButton button("Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua.");
    button.show();
    return app.exec();
}

This will create a freely resizable window with a line wrapped radio button in it:

LineWrappedRadioButton screenshot

又怨 2024-08-19 21:42:33

我成功添加了布局和标签作为单选按钮的子项,并将垂直大小策略更改为“首选”(而不是“固定”)。

不幸的是,这并没有像本机标签一样自动使其对鼠标悬停和单击做出反应,但请注意一下黑客:我为单选按钮设置了样式表(“border:none”),它开始工作。也许这是幕后发生的一些功能;我很想知道一些确定性。

并且可以使用“QRadioButton::indicator{subcontrol-position:top left}” <- http://doc.trolltech.com/qq/qq20-qss.html

I succeeded adding a layout and a label as a child for the radio button, and changing the vertical size policy to Preferred (instead of Fixed).

Unfortunately this didn't automatically make it react to the mouse hovers and clicks like the native label, but watch a hack: I setStyleSheet("border:none") for the radio button and it started working. Maybe that's some feature happening behind the scenes; I'd love to have some certainty.

And the indicator can be aligned using "QRadioButton::indicator{subcontrol-position:top left}" <- http://doc.trolltech.com/qq/qq20-qss.html

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文