2016-11-29 149 views
1

我正在嘗試使用3 QLabel比例的水平佈局使用其所有可用空間。更具體地講,這是我動態更改QLabel的字體大小以適應可用空間

Example

這就是我的目標爲

enter image description here

目前,第二圖像是通過改變qlabels與樣式表來實現一個滑塊。此外,由於我在分組內的佈局中擁有三個標籤,因此groupbox會調整大小以適應其內容,非常酷。

現在我想放棄滑塊方法,而是在移動分離器時自動填充可用空間。在this的問題中,OP重新實現了resizeEvent,我也看到其他帖子提出了相同的改變,這個while(!doesFit)或類似的點逐漸改變。

我試過使用這種方法,在resize事件和splitterMoved事件。但是,這種方法很容易產生反饋循環和其他顯示錯誤。在另一個問題中,他們建議啓用ignoreSizePolicy來防止size策略重新觸發sizeevent,但我喜歡qt如何處理佈局的大小,如何保持最小大小,然後在用戶堅持時摺疊小部件。如果HLayout會忽略由QLabels觸發的調整大小事件,也許它會起作用,這仍然是IMHO不清楚的想法。

我在想,如果這是實現此目的的推薦方式,並且存在一個不太穩定的解決方案,可能使用樣式表。有一些行爲我也可以放棄,最小大小限制(所以用戶可能會隱藏groupbox)。

如果這是推薦的方式,我應該如何使用fontmetrics,如果我有三個單獨的標籤,其中一個(數字)動態和快速地更改其文本?它不應該對性能產生影響,而且這個循環讓我很擔心。

這聽起來不像while(!fit)的方法是要削減它。還是呢?

---關於重複的問題

Another post編輯創建事件過濾器,如果返工處理與3個標籤佈局也可能會奏效。我終於使用了第一個提到的帖子的版本,並在評論中提到了帖子的變體。如果問題重新打開,我會發布答案。

+1

你試過從[這裏]解決方案(http://stackoverflow.com/questions/2202717/for-qt-4-6-x-how-to-auto-size-text-to -fit功能於一個指定的寬度)?這樣while循環就沒有必要了。 – Dusteh

+1

你只需要計算文本的縮放比例以適合可用的寬度和高度(使用[QFontMetrics](http://doc.qt.io/qt-4.8/qfontmetrics.html)) – Dusteh

+0

我喜歡這樣比循環更好。想了想,我終於必須添加'IgnoreSizePolicy',否則最小尺寸策略會阻礙。我仍然擔心穩定性思想,如果字體指標不夠精確,那麼執行會停留在那裏,並始終觸發resizeEvents? – quimnuss

回答

1

可以應用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" 
+0

感謝MWE,非常有幫助。爲什麼你會喜歡這種方法來像其他問題一樣使用fontmetrics?我可以看到你的方法更好,因爲無論標籤在哪裏,標籤都會自動進行管理,並且可能比計算機的比例因子更精確。有更多的論據嗎? – quimnuss

+0

@quimnuss我不認爲這是一種優越的方法。這只是一種恰巧可以發揮作用的方法,只要它們不會破壞牛頓算法的穩定性,就不知道這些小部件是什麼。如果小部件大小隨着字體大小的變化而增加或減少,它會找到一個解決方案。儘管如此,我仍將它歸類爲一種創可貼。對於一個真正強大的解決方案,您需要一個自定義佈局和可能的自定義小部件,以便與佈局進行正確的互操作。 –

1

Althought我認爲KubaOber的答案比較好,我會的情況下,發佈此是有幫助的人誰願意在帖子中提到的答案線的解決方案。

請注意,樣本文本也可以從標籤中檢索,樣式表中的字體以及代碼可能會放在組框或佈局的resizeEvent上。它不會在標籤的resizeEvent上工作,因爲它們會競爭空間。

這就是爲什麼KubaOber的答案更勝一籌的一個原因。我能想到的其他原因是穩定性,因爲3個標籤空間與sampletext不同,因此字體大小不如其準確。因此,重新調整大小事件可能會被字體更改再次觸發。

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); 
    } 

}