可以應用this answer中的牛頓方法來處理給定佈局中的所有小部件。它可以在任何可設置字體的小部件上工作,不僅僅在QLabel
上。
牛頓算法在給定一個好的起始點時相當快地收斂。交互式調整大小時。讓循環只執行一次不是非典型的。另一方面,QWidget::sizeHint
是整數值,並且小部件可能會舍入小數字體大小,因此有時迭代比人們預期的要慢。迭代次數被限制以確保良好的性能。
此處提供QSizeF sizeHintF()
標籤的自定義替換將更好。
小部件的最小尺寸稍微有點誇張,因爲小部件內容不會隨着小部件內容的變化而更新。不過,這可以很容易地解決。
// https://github.com/KubaO/stackoverflown/tree/master/questions/label-text-size-vert-40861305
#include <QtWidgets>
class LabelStretcher : public QObject {
Q_OBJECT
static constexpr const char kMinimumsAcquired[] = "ls_minimumsAcquired";
static constexpr const char kStretcherManaged[] = "ls_stretcherManaged";
public:
LabelStretcher(QObject *parent = 0) : QObject(parent) {
apply(qobject_cast<QWidget*>(parent));
}
void apply(QWidget *widget) {
if (!widget) return;
setManaged(widget);
setMinimumSize(widget);
widget->installEventFilter(this);
}
void setManaged(QWidget *w, bool managed = true) {
w->setProperty(kStretcherManaged, managed);
}
protected:
bool eventFilter(QObject * obj, QEvent * ev) override {
auto widget = qobject_cast<QWidget*>(obj);
if (widget && ev->type() == QEvent::Resize)
resized(widget);
return false;
}
private:
void onLayout(QLayout *layout, const std::function<void(QWidget*)> &onWidget) {
if (!layout) return;
auto N = layout->count();
for (int i = 0; i < N; ++i) {
auto item = layout->itemAt(i);
onWidget(item->widget());
onLayout(item->layout(), onWidget);
}
}
void setFont(QLayout *layout, const QFont &font) {
onLayout(layout, [&](QWidget *widget){ setFont(widget, font); });
}
void setFont(QWidget *widget, const QFont &font) {
if (!widget || !widget->property(kStretcherManaged).toBool()) return;
widget->setFont(font);
setFont(widget->layout(), font);
}
void setMinimumSize(QWidget *widget) {
if (widget->layout()) return;
widget->setMinimumSize(widget->minimumSizeHint());
}
static int dSize(const QSizeF & inner, const QSizeF & outer) {
auto dy = inner.height() - outer.height();
auto dx = inner.width() - outer.width();
return std::max(dx, dy);
}
qreal f(qreal fontSize, QWidget *widget) {
auto font = widget->font();
font.setPointSizeF(fontSize);
setFont(widget, font);
auto d = dSize(widget->sizeHint(), widget->size());
qDebug() << "f:" << fontSize << "d" << d;
return d;
}
qreal df(qreal fontSize, qreal dStep, QWidget *widget) {
fontSize = std::max(dStep + 1.0, fontSize);
return (f(fontSize + dStep, widget) - f(fontSize - dStep, widget))/dStep;
}
void resized(QWidget *widget) {
qDebug() << "pre: " << widget->minimumSizeHint() << widget->sizeHint() << widget->size();
if (!widget->property(kMinimumsAcquired).toBool()) {
onLayout(widget->layout(), [=](QWidget *widget){ setMinimumSize(widget); });
widget->setProperty(kMinimumsAcquired, true);
}
// Newton's method
auto font = widget->font();
auto fontSize = font.pointSizeF();
qreal dStep = 1.0;
int i;
for (i = 0; i < 10; ++i) {
auto prevFontSize = fontSize;
auto d = df(fontSize, dStep, widget);
if (d == 0) {
dStep *= 2.0;
continue;
}
fontSize -= f(fontSize, widget)/d;
fontSize = std::max(dStep + 1.0, fontSize);
auto change = fabs(prevFontSize - fontSize)/fontSize;
qDebug() << "d:" << d << " delta" << change;
if (change < 0.01) break; // we're within 1% of target
}
font.setPointSizeF(fontSize);
setFont(widget, font);
qDebug() << "post:" << i << widget->minimumSizeHint() << widget->sizeHint() << widget->size();
}
};
constexpr const char LabelStretcher::kMinimumsAcquired[];
constexpr const char LabelStretcher::kStretcherManaged[];
int main(int argc, char ** argv) {
QApplication app{argc, argv};
QWidget w;
QGridLayout layout{&w};
LabelStretcher stretch{&w};
QLabel labels[6];
QString texts[6] = {"V", "30.0", "kts", "H", "400.0", "ft"};
int i = 0, j = 0, k = 0;
for (auto & label : labels) {
stretch.setManaged(&label);
label.setFrameStyle(QFrame::Box);
label.setText(texts[k++]);
if (j == 0) label.setAlignment(Qt::AlignRight | Qt::AlignVCenter);
else if (j == 1) label.setAlignment(Qt::AlignCenter);
layout.addWidget(&label, i, j++);
if (j >= 3) { i++; j=0; }
}
w.show();
return app.exec();
}
#include "main.moc"
你試過從[這裏]解決方案(http://stackoverflow.com/questions/2202717/for-qt-4-6-x-how-to-auto-size-text-to -fit功能於一個指定的寬度)?這樣while循環就沒有必要了。 – Dusteh
你只需要計算文本的縮放比例以適合可用的寬度和高度(使用[QFontMetrics](http://doc.qt.io/qt-4.8/qfontmetrics.html)) – Dusteh
我喜歡這樣比循環更好。想了想,我終於必須添加'IgnoreSizePolicy',否則最小尺寸策略會阻礙。我仍然擔心穩定性思想,如果字體指標不夠精確,那麼執行會停留在那裏,並始終觸發resizeEvents? – quimnuss