[SOLVED] Scaled background image using stylesheet



  • How can I make a scaled background image using stylesheet?

    I tried to use:
    [code]#centralWidget {
    background: url(:/res/background.jpg) no-repeat center center fixed;
    background-size: cover;
    }[/code]

    But it seems that Qt ignores background-size: cover; so I wonder is there any trick I can do?

    P.S. I want a similar behavior of the background in "this page":http://css-tricks.com/examples/FullPageBackgroundImage/progressive.php


  • Lifetime Qt Champion

    Hi,

    AFAIK, background-size is not supported by Qt's style-sheet

    Maybe image might do the trick ?

    Hope it helps


  • Moderators

    background-size is not supported but there's a trick with border-image ;)
    @
    #centralWidget {
    border-image: url(:/res/background.jpg) 0 0 0 0 stretch stretch;
    }
    @



  • [quote author="SGaist" date="1397160051"]
    Maybe image might do the trick ?
    [/quote]

    Actually I saw this trick before but unfortuantely I missed it; for that I asked this question here.



  • [quote author="Chris Kawa" date="1397169811"]background-size is not supported but there's a trick with border-image ;)
    @
    #centralWidget {
    border-image: url(:/res/background.jpg) 0 0 0 0 stretch stretch;
    }
    @[/quote]

    Unfortunately it doesn't help because it doesn't keep same aspect ratio as shown in the link I pointed it above ( http://css-tricks.com/examples/FullPageBackgroundImage/progressive.php ).

    I need a background image scaled with keeping on same aspect ratio.



  • Hi, if using the styleSheet is not mandatory: There a some good tips to sizing an image with the right aspect ratio in the Qt Image Viewer Example.


  • Moderators

    Yeah, you won't get the exactly same effect with border-image, but you can change one of the dimensions from stretch to repeat.
    This would keep the aspect ratio. If that's no good I would just override paintEvent and draw it however you need with QPainter.



  • [quote author="Chris Kawa" date="1397209085"]If that's no good I would just override paintEvent and draw it however you need with QPainter.[/quote]

    Unfortunately I couldn't find a solution using qss so may you please guide from where I can start by using paintEvent.



  • I fixed this issue by using the following:

    [code]void MainWindow::paintEvent(QPaintEvent *pe)
    {
    QPixmap pixmap;
    pixmap.load(":/res/background.jpg");
    QPainter paint(this);
    int widWidth = this->ui->centralWidget->width();
    int widHeight = this->ui->centralWidget->height();
    pixmap = pixmap.scaled(widWidth, widHeight, Qt::KeepAspectRatioByExpanding);
    paint.drawPixmap(0, 0, pixmap);
    QWidget::paintEvent(pe);
    }[/code]

    P.S. I feel very upset because I hoped to use QSS, I always prefer to use it instead of manual theme manipulation .


  • Moderators

    A comment on your paintEvent(). At the end you call QWidget::paintEvent(). This is not a good idea. If someday you flip the autoFillBackground flag or a future theme adds some background to the widgets, it will paint over whatever you draw.
    Either call it before you draw or, if you're filling the whole area anyway, don't call it at all.
    Also you can just do QPixmap pixmap(":/res/background.jpg"), no need to load separately. You could also store it somewhere and not load again each time it's drawn.

    As for you being upset - cheer up :) QSS just doesn't support it so there's nothing you can do about it. Unless of course you want to implement it and offer a patch to Qt ;)
    And it's not like it's slower or anything. It just looks funny.
    If you don't want to mix this drawing code into your widget there's another way.
    Make a class that derives from QObject and implement eventFilter method that will do the drawing for the paint event. Then you can install instance of that class on your widget. Something ike this:
    @SuperBackgroundPainter blah(":/res/background.jpg");
    MainWindow w;
    w.installEventFilter(blah);
    w.show();
    ...@
    This way you can nicely separate this unfortunate painting stuff from the main purpose of your widget.



  • [quote author="Chris Kawa" date="1397324827"]A comment on your paintEvent(). At the end you call QWidget::paintEvent(). This is not a good idea. If someday you flip the autoFillBackground flag or a future theme adds some background to the widgets, it will paint over whatever you draw.
    Either call it before you draw or, if you're filling the whole area anyway, don't call it at all.
    Also you can just do QPixmap pixmap(":/res/background.jpg"), no need to load separately. You could also store it somewhere and not load again each time it's drawn.[/quote]

    I appreciate your comment Chris; Thank you :)

    Below the solution depending on your advice:

    [code]
    MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
    ui->setupUi(this);
    _pixmapBg.load(":/res/background.jpg");
    }

    void MainWindow::paintEvent(QPaintEvent *pe)
    {
    QPainter paint(this);
    int widWidth = this->ui->centralWidget->width();
    int widHeight = this->ui->centralWidget->height();
    _pixmapBg = _pixmapBg.scaled(widWidth, widHeight, Qt::KeepAspectRatioByExpanding);
    paint.drawPixmap(0, 0, _pixmapBg);
    }[/code]

    A Side discussion:
    The solution above scales the pixmap from (0,0) do you've any idea how to scale the pixmap from the center of the pixmap?



  • [quote author="mbnoimi" date="1397350097"]
    A Side discussion:
    The solution above scales the pixmap from (0,0) do you've any idea how to scale the pixmap from the center of the pixmap?[/quote]

    Forget it guys, I found out the solution as following:

    [code]void MainWindow::paintEvent(QPaintEvent *pe)
    {
    QPainter paint(this);
    int widWidth = this->ui->centralWidget->width();
    int widHeight = this->ui->centralWidget->height();
    _pixmapBg = _pixmapBg.scaled(widWidth, widHeight, Qt::KeepAspectRatioByExpanding);
    QPoint centerOfWidget = ui->centralWidget->rect().center();
    QRect rectOfPixmap = _pixmapBg.rect();
    rectOfPixmap.moveCenter(centerOfWidget);
    paint.drawPixmap(rectOfPixmap.topLeft(), _pixmapBg);
    }[/code]


  • Moderators

    Yeah, another thing I noticed now is that you're using scaled(). That's also creating a copy of the image, thus is not very good performance-wise. It's better to just use one of the drawPixmap methods with explicit dimensions.
    Besides the way you use scaled() is no good. If your image is 1000x1000 and you scale it to 10x10 and then back to say 500x500 it will be ugly blurred.

    Calculating dimensions might be a little harder, but is a lot faster than copying bitmaps around.

    This is one way to do the drawing (sorry if it's not perfect, It's kinda brain-fart :) )

    @
    QPainter painter(this);

    auto winSize = size();
    auto pixmapRatio = (float)_pixmapBg.width() / _pixmapBg.height();
    auto windowRatio = (float)winSize.width() / winSize.height();

    if(pixmapRatio > windowRatio)
    {
    auto newWidth = (int)(winSize.height() * pixmapRatio);
    auto offset = (newWidth - winSize.width()) / -2;
    painter.drawPixmap(offset, 0, newWidth, winSize.height(), _pixmapBg);
    }
    else
    {
    auto newHeight = (int)(winSize.width() / pixmapRatio);
    painter.drawPixmap(0, 0, winSize.width(), newHeight, _pixmapBg);
    }
    @



  • Thank you but it doesn't show it in the cente.

    Any way loading _pixmapBg inside paintEvent() will fix scaled() issue.


  • Moderators

    Didn't noticed it should also center vertically. Easy fix:
    @
    else
    {
    auto newHeight = (int)(winSize.width() / pixmapRatio);
    auto offset = (newHeight - winSize.height()) / -2;
    painter.drawPixmap(0, offset, winSize.width(), newHeight, _pixmapBg);
    }
    @
    As said before loading it and then scaling inside paint event is not a good idea. It's IO and then also copying. You're doing a lot of the same work over and over again.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.