[英]Dynamically change font size of QLabel to fit available space
我正在嘗試使3 QLabel
比例尺的水平布局使用其所有可用空間。 更具體地說,這就是我所擁有的
這就是我的目標
目前,通過使用滑塊更改qlabel的樣式表來獲得第二張圖像。 另外,由於我在組框內的布局中具有三個標簽,因此組框的大小可以調整以適合其內容,很酷。
現在,我想放棄滑塊方法,而是在移動拆分器時自動調整可用空間。 在這個問題中,OP重新實現了resizeEvent
,並且我看到其他帖子建議使用while( !doesFit )
或類似內容逐點更改。
我在調整大小事件和splitterMoved事件上都嘗試使用這種方法。 但是,這種方法很容易導致反饋循環和其他顯示錯誤。 在另一個問題中,他們建議啟用ignoreSizePolicy以防止大小策略重新觸發sizeevent,但是我喜歡qt如何處理布局的大小,如何保持最小大小,然后在用戶堅持的情況下折疊小部件。 如果HLayout
忽略由QLabels
觸發的調整大小事件,那還是QLabels
,恕我直言,這是不干凈的。
我想知道這是否是實現此目標的推薦方法,並且是否存在不太不穩定的解決方案,可能使用樣式表。 我還可以刪除某些行為,即最小大小限制(以便用戶可能隱藏組框)。
如果這是推薦的方式,如果我有三個單獨的標簽,其中一個(數字)可以快速,動態地更改其文本,那么應該如何使用字體度量? 它不應該對性能產生影響, while
循環使我警惕。
聽起來不像while(!fit)
方法會減少它。 還是呢?
---編輯有關重復的問題
另一篇文章創建了一個事件過濾器,如果重新處理帶有3個標簽的布局,它也可能會起作用。 我最終使用了第一個提到的帖子的版本,並加上了評論中提到的帖子的變體。 如果問題重新開放,我將發布答案。
可以從這個答案中應用牛頓方法方法來處理給定布局中的所有小部件。 它不僅可以在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"
盡管我認為KubaOber的答案更好,但我會發布此文章,以防它對在帖子中提到的答案中想要解決方案的人有所幫助。
請注意,也可以從標簽中檢索樣本文本,從樣式表中檢索字體,並且可以將代碼潛在地放在組框或布局的resizeEvent
上。 由於標簽會爭奪空間,因此對標簽的resizeEvent
。
這就是KubaOber的答案出眾的原因之一。 考慮到3個標簽空間與樣本文本不同,我可以想到的其他原因是穩定性,因此字體大小不盡如人意。 因此,字體更改可能會再次觸發調整大小事件。
static void fitGroupBoxLabels(QGroupBox* groupbox, const QFont &samplefont, const QLayout* const samplelayout)
{
groupbox->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
QString sampletext = "V 1000.0 kts";
QRect availablerect = samplelayout->contentsRect();
if(samplefont.pointSizeF() <= 0) return; //not initalized yet, return
QRect textrect = QFontMetrics(samplefont).boundingRect(sampletext);
if(!textrect.isValid() || !availablerect.isValid()) return; //not initalized yet, return
float factorh = availablerect.width() / (float)textrect.width();
float factorw = availablerect.height() / (float)textrect.height();
float factor = std::min(factorh, factorw);
if (factor < 0.95 || factor > 1.05)
{
float fontSize = samplefont.pointSizeF()*factor;
QString groupBoxStyle = QString("QGroupBox{font-size:8pt} QLabel{font-size:%1pt}").arg(fontSize);
groupbox->setStyleSheet(groupBoxStyle);
}
}
解決此問題后,我創建了DynamicFontSizeLabel和DynamicFontSizePushButton小部件。 希望能幫助到你。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.