Syncing Label fontsize with layout in PyQt

2019-05-31 06:28发布

问题:

What is the concrete way to change the font size of a label to match the layout size its contained in through signal/slots?

回答1:

Below is a solution, for a QLabel, derived from the solution posted here: https://forum.qt.io/topic/36088/automatically-scale-text-in-qlabels/5.

This consists in a reimplementation of the resizeEvent method where the font size of the QLabel is updated according to the size of its contentRect. Note that the sizePolicy of the Qlabel has to be set to Ignored for this to work properly.

import sys
from PyQt4 import QtGui     

class myQLabel(QtGui.QLabel):
    def __init__(self, *args, **kargs):
        super(myQLabel, self).__init__(*args, **kargs)

        self.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Ignored,
                                             QtGui.QSizePolicy.Ignored))  

        self.setMinSize(14)

    def setMinSize(self, minfs):        

        f = self.font()
        f.setPixelSize(minfs)
        br = QtGui.QFontMetrics(f).boundingRect(self.text())

        self.setMinimumSize(br.width(), br.height())

    def resizeEvent(self, event):
        super(myQLabel, self).resizeEvent(event)

        if not self.text():
            return

        #--- fetch current parameters ----

        f = self.font()
        cr = self.contentsRect()

        #--- find the font size that fits the contentsRect ---

        fs = 1                    
        while True:

            f.setPixelSize(fs)
            br =  QtGui.QFontMetrics(f).boundingRect(self.text())

            if br.height() <= cr.height() and br.width() <= cr.width():
                fs += 1
            else:
                f.setPixelSize(max(fs - 1, 1)) # backtrack
                break  

        #--- update font size ---

        self.setFont(f)     


class myApplication(QtGui.QWidget):
    def __init__(self, parent=None):
        super(myApplication, self).__init__(parent)

        #---- Prepare a Layout ----

        grid = QtGui.QGridLayout()  

        for i in range(3):
            grid.addWidget(myQLabel('some text'), i, 0)  
            grid.setRowStretch(i, i+1)
            grid.setRowMinimumHeight(i, 25)

        self.setLayout(grid)
        self.resize(500, 300)


if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)

    instance = myApplication()  
    instance.show()    

    sys.exit(app.exec_())

Which results in:

Update - Optimization of resizeEvent :

Below is an optimized version of the resizeEvent method that should yield better performances. It drastically reduces the number of iteration required to find the optimal value of the font size. I haven't tested it extensively though.

def resizeEvent(self, event):
    super(myQLabel, self).resizeEvent(event)        

    if not self.text():
        return

    #--- fetch current parameters ----

    f = self.font()
    cr = self.contentsRect()

    #--- iterate to find the font size that fits the contentsRect ---

    dw = event.size().width() - event.oldSize().width()   # width change
    dh = event.size().height() - event.oldSize().height() # height change

    fs = max(f.pixelSize(), 1)        
    while True:

        f.setPixelSize(fs)
        br =  QtGui.QFontMetrics(f).boundingRect(self.text())

        if dw >= 0 and dh >= 0: # label is expanding

            if br.height() <= cr.height() and br.width() <= cr.width():
                fs += 1
            else:
                f.setPixelSize(max(fs - 1, 1)) # backtrack
                break                    

        else: # label is shrinking

            if br.height() > cr.height() or br.width() > cr.width():
                fs -= 1
            else:
                break

        if fs < 1: break

    #--- update font size ---           

    self.setFont(f)