QCheckbox/QRadioButton перенос строки Qt4.6.0
Я пытаюсь использовать многострочный флажок / радиокнопку с Qt, используя стандартную QCheckbox/ QRadioButton.
Я не нашел прямого решения с тех пор QRadioButton{wrap:true;}
не имеет никакого эффекта Единственное, что можно было бы получить доступ к QRadioButton->label->setLineWrap(true)
но
- Я хотел бы сделать это от дизайнера
- не нужно переписывать виджет
Любая идея помимо размещения QRadioButton и QLabel рядом друг с другом?
Спасибо, Борис.
5 ответов
Это действительно раздражает и не может быть решено без повторной реализации: лейбл использует Qt::PlainText
если я не ошибаюсь. В наших проектах команда UI решила это двумя способами.
Использование кнопки QRadioButton в качестве заголовка
Макет с использованием QRadioButton в качестве заголовка http://i48.tinypic.com/97prtl.png
Перепишите текст QRadioButton таким образом, чтобы в нем было только краткое описание опции. Поместите QLabel под ним и добавьте более подробное описание. Мы используем меньший шрифт и небольшой отступ, чтобы он выглядел аккуратно. Это часто используется в Mac OS X, например.
Удаление текста с переключателя
http://i46.tinypic.com/wwhrgh.png
Передайте интерфейс так, чтобы каждая радиокнопка располагалась слева от QLabel. Добавьте весь текст в QLabel и установите метку как собеседника с переключателями. Это наименее функциональный подход, потому что нажатие на ярлык не проверяет переключатель. Кроме того, выравнивание не очень хорошо.
Единственный способ, которым я знаю (но это не "Layoutable"), это поставить знак \n в строку.
Проблема
К сожалению, для этого нет короткого и простого решения. Об этом есть запрос функции Qt (см. QTBUG-5370), но он находится на рассмотрении уже 13 (!) лет, так что, вероятно, он никогда не будет реализован.
Поэтому мы должны реализовать это самостоятельно. Чистый способ - справиться сresizeEvent
в рамках обычаяQRadioButton
подкласс, чтобы выполнить там необходимый перенос строк. В дополнение к этому вы также должны использоватьsetSizePolicy
и переопределитьminimumSizeHint
переключателя.
Полная реализацияLineWrappedRadioButton
приведен ниже.
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();
}
Используйте это так
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();
}
Это даст следующий результат:
Мне удалось добавить макет и метку в качестве дочернего элемента для переключателя и изменить политику размера по вертикали на Предпочитаемый (вместо фиксированного).
К сожалению, это не заставило его автоматически реагировать на зависание и щелчки мыши, как на нативном ярлыке, но понаблюдать за хаком: я установил переключатель ("border:none") для переключателя, и он начал работать. Может быть, какая-то особенность происходит за кулисами; Я хотел бы иметь некоторую уверенность.
И индикатор можно выровнять, используя "QRadioButton:: Indicator {subcontrol-position: top left}" <- http://doc.trolltech.com/qq/qq20-qss.html
Мое решение:
#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
}