PDA

View Full Version : Scaling Background Image behind Widgets / dynamic stylesheet resources ?



PEM
17th June 2010, 21:57
I'm simply trying to show a (nicely scaled) image as the background of some kind of container, with layout managed widgets (buttons).

This use of stylesheets works perfectly :

m_ui->my_frame->setStyleSheet("image: url(myGraphic.jpg); background-repeat: repeat;");

Unfortunately, this code won't work for me. I need to load the graphic dynamically (using QImage or QPixmap, say), not from the resource management system or the hard-drive, and I can't see how to do that with stylesheets.

Deriving a custom Frame, and writing a special also works, though it feels a lot like a hack, and causes performance issues when the Frame is re-sized. (in the code below, the custom Frame has a pixmap variable that has been pre-loaded with the graphic):

void BackgroundFrame::paintEvent(QPaintEvent *pe)
{
if(!pixmap.isNull())
{
QPainter myPainter(this);
pixmap.scaled(width(),height(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
myPainter.drawPixmap(0,0, pixmap);
}
QFrame::paintEvent(pe);
}

I feel that there should be some way to do this with just a couple lines of code and/ or the simple QT designer built into QT Creator, but I haven't been able to find any examples.

What is the correct way to do this?

ChrisW67
18th June 2010, 04:23
You wanted code tags not qtclass tags.


This use of stylesheets works perfectly :

m_ui->my_frame->setStyleSheet("image: url(myGraphic.jpg); background-repeat: repeat;");
Unfortunately, this code won't work for me. I need to load the graphic dynamically (using QImage or QPixmap, say), not from the resource management system or the hard-drive, and I can't see how to do that with stylesheets.

Assuming the background will not change often then you could write the image to a temporary file and dynamically update the style string to point at it.

PEM
18th June 2010, 14:36
You wanted code tags not qtclass tags.
My bad, won't happen again. (thanks for the heads up)


Assuming the background will not change often then you could write the image to a temporary file and dynamically update the style string to point at it.
There are a couple reasons I didn't want to do that.

1) The background will change as often as the user presses the next button. This could end up being three or four times in a second, depending on what the user is trying to do.
2) Writing/Reading to/from a temporary file on the hard-drive when you already have the graphic loaded into memory doesn't seem like a reasonable solution, and could (potentially) cause permission-related difficulties down the road.

Is there really no way to reference an object in memory instead of a file for stylesheets? How would someone load a skin from a database?

numbat
18th June 2010, 15:14
You can do this with a QAbstractFileEngine. Following is code:
Header:


#ifndef TEST14_H
#define TEST14_H

#include <QtGui/QMainWindow>
#include <QAbstractFileEngineHandler>
#include <QAbstractFileEngine>
#include <QDateTime>
#include <QByteArray>
#include <QtAlgorithms>

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
MainWindow(QWidget *parent = 0);
~MainWindow();
};

class NumbatEngineHandler : public QAbstractFileEngineHandler
{
public:
QAbstractFileEngine *create(const QString &fileName) const;
};

class NumbatEngine : public QAbstractFileEngine
{
public:
NumbatEngine(const QString& fileName);
bool caseSensitive () const { return false; }
bool close () { return true; }
QStringList entryList ( QDir::Filters filters, const QStringList & filterNames ) const { return QStringList(); }
FileFlags fileFlags ( FileFlags type = FileInfoAll ) const
{ return QAbstractFileEngine::ReadOtherPerm | QAbstractFileEngine::ExeOtherPerm | QAbstractFileEngine::FileType | QAbstractFileEngine::WriteOtherPerm; }
QString fileName ( FileName file = DefaultName ) const { return m_fileName; }
QDateTime fileTime ( FileTime time ) const { return QDateTime(); }
bool mkdir ( const QString & dirName, bool createParentDirectories ) const { return false; }
QString owner ( FileOwner owner ) const { return QString(); }
uint ownerId ( FileOwner owner ) const { return -2; }
bool remove () { return false; }
bool rename ( const QString & newName ) { return false; }
bool rmdir ( const QString & dirName, bool recurseParentDirectories ) const { return false; }
void setFileName ( const QString & file ) { m_fileName = file; }
bool setPermissions ( uint perms ) { return false; }
bool setSize ( qint64 size ) { m_bytes->resize(size); return true; }
qint64 size () const { return m_bytes->size(); }
bool seek ( qint64 offset ) { m_filePos = offset; return true; }
bool open ( QIODevice::OpenMode mode ) { return true; }
bool isRelativePath () const { return false; }
qint64 write ( const char * data, qint64 len );
qint64 read ( char * data, qint64 maxlen );

private:
QByteArray* m_bytes;
QString m_fileName;
qint64 m_filePos;
};

#endif // TEST14_H

Source:


#include "Test14.h"
#include <QMap>
#include <QPixmap>
#include <QLabel>
#include <QApplication>
#include <QDebug>

QAbstractFileEngine * NumbatEngineHandler::create(const QString &fileName) const
{
// We handle all files which start with ???
if (fileName.toLower().startsWith("???"))
return new NumbatEngine(fileName);
else
return 0;
}


NumbatEngine::NumbatEngine(const QString& fileName)
: QAbstractFileEngine(), m_fileName(fileName), m_filePos(0)

{
static QMap<QString, QByteArray*> map;

if (map.contains(m_fileName))
{
m_bytes = map.value(m_fileName);
}
else
{
m_bytes = new QByteArray;
map[m_fileName] = m_bytes;
}
}

qint64 NumbatEngine::write ( const char * data, qint64 len )
{
if (m_filePos + len > m_bytes->size()) m_bytes->resize(m_filePos + len);

qCopy(data, data + len, m_bytes->data() + m_filePos);

m_filePos += len;

return len;
}

qint64 NumbatEngine::read ( char * data, qint64 maxlen )
{
qint64 readBytes;

if (m_filePos + maxlen > m_bytes->size())
readBytes = m_bytes->size() - m_filePos;
else
readBytes = maxlen;

qCopy(m_bytes->constData() + m_filePos,
m_bytes->constData() + m_filePos + readBytes, data);

m_filePos += readBytes;

return readBytes;
}



/* Demonstration. */
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QPixmap pix("image.jpeg");
pix.save("???Hello.jpg");

QLabel * lbl = new QLabel;
lbl->setStyleSheet("QLabel {background-image:url(\"???Hello.jpg\");}");
setCentralWidget(lbl);
}


MainWindow::~MainWindow()
{
}

int main(int argc, char **argv)
{
QApplication app(argc, argv);

NumbatEngineHandler engine;

MainWindow window;
window.show();

return app.exec();
}

Flakes
21st March 2011, 14:22
Hi,

for some reason stylesheet doesn't want to display files from NumbatEngine. Copying file back from NumbatEngine to hard disk as following writes the file just fine, i.e. it's not damaged or something:



QPixmap pixFromNumbat("???Hello.jpg");
pixFromNumbat.save("pixFromNumbat.jpg");


Also, stylesheet does display the image if it's read from hard disk (by disabling NumbatEngine from main()).

I'm using Qt 4.7.2. Please, can anyone confirm that NumbatEngine works for you? I'm desperate :(

EDIT
This works too (taken from (http://www.qtcentre.org/threads/22947-Accessing-generated-pixmaps-in-html-Custom-tooltips?p=111938#post111938)):


/* Demonstration. */
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QPixmap pix("image.jpeg");
pix.save("???Hello.jpg");

QLabel * lbl = new QLabel;
//lbl->setStyleSheet("QLabel {background-image:url(\"???Hello.jpg\");}");
lbl->setText("<b> this is a test</b><img src=\"???Hello.jpg\" />");
setCentralWidget(lbl);
}


but it doesn't work for styleSheet. Bug in Qt?

Elv13
10th October 2012, 21:05
For posterity and because Google return this as the first result, the solution is to edit the widget palette with a brush using a QImage as "stamp".


QPalette p = palette();
QBrush brush = QBrush(generateMyQImage());
p.setBrush(QPalette::Base, brush);
p.setBrush(QPalette::Window, brush);
setPalette(p);
setAutoFillBackground(true);