[SOLVED] Scaled background image using stylesheet
-
How can I make a scaled background image using stylesheet?
I tried to use:
#centralWidget { background: url(:/res/background.jpg) no-repeat center center fixed; background-size: cover; }
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
-
Hi,
AFAIK, background-size is not supported by Qt's style-sheet
Maybe image might do the trick ?
Hope it helps
-
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; }
-
@chrisaverage said:
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;
}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.
-
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. -
@chrisaverage said:
If that's no good I would just override paintEvent and draw it however you need with QPainter.
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:
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); }
P.S. I feel very upset because I hoped to use QSS, I always prefer to use it instead of manual theme manipulation .
-
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.
-
@chrisaverage said:
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.I appreciate your comment Chris; Thank you :)
Below the solution depending on your advice:
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); }
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? -
@mbnoimi said:
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?Forget it guys, I found out the solution as following:
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); }
-
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); }
-
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.
-
Hi,
Not knowing anything about the code you are using, I cannot answer.
Since you necro-posted this thread, did any of the advices put here help ? Do they relate to your issue ?