Here is a possible implementation of a QLabel subclass that scales its pixmap content while keeping its aspect ratio. It is implemented based on the way QLabel::paintEvent is implemented.
If you are using it in a layout you can also set its size policy to QSizePolicy::Expanding, so that additional space in the layout is taken up by the QLabel for a larger display of the pixmap content.
#include <QApplication>
#include <QtWidgets>
class PixmapLabel : public QLabel{
public:
    explicit PixmapLabel(QWidget* parent=nullptr):QLabel(parent){
        //By default, this class scales the pixmap according to the label's size
        setScaledContents(true);
    }
    ~PixmapLabel(){}
protected:
    void paintEvent(QPaintEvent* event);
private:
    //QImage to cache the pixmap()
    //to avoid constructing a new QImage on every scale operation
    QImage cachedImage;
    //used to cache the last scaled pixmap
    //to avoid calling scale again when the size is still at the same
    QPixmap scaledPixmap;
    //key for the currently cached QImage and QPixmap
    //used to make sure the label was not set to another QPixmap
    qint64 cacheKey{0};
};
//based on the implementation of QLabel::paintEvent
void PixmapLabel::paintEvent(QPaintEvent *event){
    //if this is assigned to a pixmap
    if(pixmap() && !pixmap()->isNull()){
        QStyle* style= PixmapLabel::style();
        QPainter painter(this);
        drawFrame(&painter);
        QRect cr = contentsRect();
        cr.adjust(margin(), margin(), -margin(), -margin());
        int align= QStyle::visualAlignment(layoutDirection(), alignment());
        QPixmap pix;
        if(hasScaledContents()){ //if scaling is enabled
            QSize scaledSize= cr.size() * devicePixelRatioF();
            //if scaledPixmap is invalid
            if(scaledPixmap.isNull() || scaledPixmap.size()!=scaledSize
                    || pixmap()->cacheKey()!=cacheKey){
                //if cachedImage is also invalid
                if(pixmap()->cacheKey() != cacheKey){
                    //reconstruct cachedImage
                    cachedImage= pixmap()->toImage();
                }
                QImage scaledImage= cachedImage.scaled(
                            scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
                scaledPixmap= QPixmap::fromImage(scaledImage);
                scaledPixmap.setDevicePixelRatio(devicePixelRatioF());
            }
            pix= scaledPixmap;
        } else { // no scaling, Just use pixmap()
            pix= *pixmap();
        }
        QStyleOption opt;
        opt.initFrom(this);
        if(!isEnabled())
            pix= style->generatedIconPixmap(QIcon::Disabled, pix, &opt);
        style->drawItemPixmap(&painter, cr, align, pix);
    } else { //otherwise (if the label is not assigned to a pixmap)
        //call base paintEvent
        QLabel::paintEvent(event);
    }
}
//DEMO program
QPixmap generatePixmap(QSize size) {
    QPixmap pixmap(size);
    pixmap.fill(Qt::white);
    QPainter p(&pixmap);
    p.setRenderHint(QPainter::Antialiasing);
    p.setPen(QPen(Qt::black, 10));
    p.drawEllipse(pixmap.rect());
    p.setPen(QPen(Qt::red, 2));
    p.drawLine(pixmap.rect().topLeft(), pixmap.rect().bottomRight());
    p.drawLine(pixmap.rect().topRight(), pixmap.rect().bottomLeft());
    return pixmap;
}
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QPixmap pixmap= generatePixmap(QSize(1280, 960));
    PixmapLabel label;
    label.setPixmap(pixmap);
    label.setAlignment(Qt::AlignCenter);
    label.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    label.setMinimumSize(320, 240);
    label.show();
    return a.exec();
}
I think this is better than the solution in this answer, as the QLabel here takes care of resizing its pixmap. So, there is no need to resize it manually every time the parent widget is resized  and everytime a new pixmap is set on it.