Background image not showing on QWidget - qt

I am designing a window using QWidget and set a background image, when i run my code i am not getting background image but showing window with default background.
Can anyone help me what may be the reason.
// In header file
class STUDY : public QMainWindow, public Ui::STUDYClass
{
Q_OBJECT
public:
STUDY(QWidget *parent = 0, Qt::WFlags flags = 0);
~STUDY();
QPaintEvent *p2;
void backgroundImage();
void paintEvent(QPaintEvent *);
public slots:
};
//Constructor and paintEvent function in Cpp file
STUDY::STUDY(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
setupUi(this);
backgroundImage();
update();
paintEvent(p2);
}
void STUDY::paintEvent(QPaintEvent *p2)
{
QPixmap pixmap;
pixmap.load(":/STUDY/Resources/Homepage.png");
QPainter paint(this);
paint.drawPixmap(0, 0, pixmap);
QWidget::paintEvent(p2);
}

There are many ways to set the background color to window,
I will give you one simple technique. i.e Override, the paintEvent of the QWidget. and draw the pixmap there.
Here is the sample widget code, i hope it helps
Header file
#ifndef QBACKGROUNDIMAGE_H
#define QBACKGROUNDIMAGE_H
#include <QtGui/QMainWindow>
#include "ui_QbackgroundImage.h"
#include <QtGui>
class backgroundImgWidget;
class QbackgroundImage : public QMainWindow
{
Q_OBJECT
public:
QbackgroundImage(QWidget *parent = 0);
~QbackgroundImage();
private:
Ui::QbackgroundImage ui;
};
class backgroundImgWidget : public QWidget
{
Q_OBJECT
public:
backgroundImgWidget(QWidget *parent = 0);
~backgroundImgWidget();
protected:
void paintEvent(QPaintEvent *p2);
};
#endif // QBACKGROUNDIMAGE_H
CPP file
#include "QbackgroundImage.h"
QbackgroundImage::QbackgroundImage(QWidget *parent)
: QMainWindow(parent)
{
// ui.setupUi(this);
backgroundImgWidget* widget = new backgroundImgWidget();
setCentralWidget(widget);
}
QbackgroundImage::~QbackgroundImage()
{
}
backgroundImgWidget::backgroundImgWidget(QWidget *parent):QWidget(parent)
{
}
backgroundImgWidget::~backgroundImgWidget()
{
}
void backgroundImgWidget::paintEvent(QPaintEvent *p2)
{
QPixmap pixmap;
pixmap.load(":/new/prefix1/Sunset.jpg");
QPainter paint(this);
paint.drawPixmap(0, 0, pixmap);
QWidget::paintEvent(p2);
}

You can reimplement paintEvent:
void Widget::paintEvent( QPaintEvent* e )
{
QPainter painter( this );
painter.drawPixmap( 0, 0, QPixmap(":/new/prefix1/picture001.png").scaled(size()));
QWidget::paintEvent( e );
}

Related

How to change QPainterPath color after clicking on QPushButton

I subclassed a QPushButton so that I was able to re-implement the paintEvent(QPaintEvent *paint) method also advised by the official documentation.
Below is the sequence of operations:
a) After I launch the application the button is below:
b) This is after I hover on it:
c) then I click the button:
d) and finally I release the mouse
e) go away from the button
However the problem is that the QPainterPath with which I designed the green box it should be red after I release the button. And, of course, it should become green again after I click again the button.
Below the code:
custombutton.h
class CustomButton : public QPushButton
{
Q_OBJECT
public:
CustomButton(QWidget *parent = nullptr);
~CustomButton();
QString FirstName = "MACHINE";
QString LastName = "CONTROL";
protected:
void paintEvent(QPaintEvent *);
};
custombutton.cpp
CustomButton::CustomButton(QWidget *parent) : QPushButton(parent)
{
setGeometry(150, 150, 110, 110);
setAttribute(Qt::WA_TranslucentBackground);
setStyleSheet(
"QPushButton{background-color: lightGray;border: 1px solid black; border-radius: 5px;}"
"QPushButton:hover{background-color: gray;}"
"QPushButton:pressed{background-color: lightGray;}");
}
CustomButton::~CustomButton()
{
}
void CustomButton::paintEvent(QPaintEvent *paint)
{
QPushButton::paintEvent(paint);
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
QPainterPath path;
path.addRoundedRect(QRectF(15, 15, 80, 5), 2, 2);
QPen pen(Qt::black, 0.3);
p.setPen(pen);
p.fillPath(path, Qt::darkGreen);
p.drawPath(path);
p.save();
p.drawText(QPoint(20, 60), FirstName);
p.drawText(QPoint(20, 80), LastName);
p.setFont(QFont("Arial", 10));
p.restore();
}
mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
public slots:
void onClickedButton();
private:
Ui::MainWindow *ui;
CustomButton *newBtn;
};
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
newBtn = new CustomButton(this);
newBtn->show();
connect(newBtn, &QPushButton::clicked, this, &MainWindow::onClickedButton);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::onClickedButton()
{
QPushButton* target = qobject_cast<QPushButton*>(sender());
if (target != nullptr)
{
QPainter p;
p.setRenderHint(QPainter::Antialiasing);
QPainterPath path;
path.addRoundedRect(QRectF(15, 15, 80, 5), 2, 2);
QPen pen(Qt::black, 0.3);
p.setPen(pen);
p.fillPath(path, Qt::darkRed);
p.drawPath(path);
p.save();
p.restore();
}
}
As you see in the MainWindow I created a function onClickedButton() which I thought would have triggered the QPainterPath into red after clicked. And in order to do that I casted it into an object (qobject_cast). But unfortunately it didn't work as expexted.
The solution is to create a flag that indicates the color and call update() for repainting:
#ifndef CUSTOMBUTTON_H
#define CUSTOMBUTTON_H
#include <QPushButton>
class CustomButton : public QPushButton
{
Q_OBJECT
public:
CustomButton(QWidget *parent = nullptr);
~CustomButton();
QString FirstName = "MACHINE";
QString LastName = "CONTROL";
private Q_SLOTS:
void handleClicked();
protected:
void paintEvent(QPaintEvent *);
private:
bool state;
};
#endif // CUSTOMBUTTON_H
#include "custombutton.h"
#include <QPainter>
#include <QPainterPath>
CustomButton::CustomButton(QWidget *parent) : QPushButton(parent), state(true)
{
setGeometry(150, 150, 110, 110);
setAttribute(Qt::WA_TranslucentBackground);
setStyleSheet(
"QPushButton{background-color: lightGray;border: 1px solid black; border-radius: 5px;}"
"QPushButton:hover{background-color: gray;}"
"QPushButton:pressed{background-color: lightGray;}");
connect(this, &QPushButton::clicked, this, &CustomButton::handleClicked);
}
CustomButton::~CustomButton()
{
}
void CustomButton::handleClicked()
{
state = !state;
update();
}
void CustomButton::paintEvent(QPaintEvent *paint)
{
QPushButton::paintEvent(paint);
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
QPainterPath path;
path.addRoundedRect(QRectF(15, 15, 80, 5), 2, 2);
QPen pen(Qt::black, 0.3);
p.setPen(pen);
p.fillPath(path, state ? Qt::darkGreen: Qt::darkRed);
p.drawPath(path);
p.drawText(QPoint(20, 60), FirstName);
p.drawText(QPoint(20, 80), LastName);
p.setFont(QFont("Arial", 10));
}

QWidget background images not appearing when parent exists

I have a simple widget which uses an image as background and contains some child widgets. When I create it without a parent (as a dialogue) everything is perfect. But if I create it as a child of some other widget, I can't see the background.
Can I use QWidget::setPalette to set the background for a child widget?
If not, how would you accomplish this?
#include <QWidget>
#include <QPixmap>
#include <QPalette>
#include <QLabel>
class Panel : public QWidget
{
Q_OBJECT
public:
explicit Panel(QWidget *parent = 0) QWidget(parent)
{
bgnd_ = new QPixmap(":/path/to/image.png");
PaintBackground();
QLabel* lbl = new QLabel("SomeChild",this);
}
private:
void PaintBackground()
{
QPixmap bgnd = bgnd_->scaled(this->size(), Qt::IgnoreAspectRatio);
QPalette palette;
palette.setBrush(QPalette::Background, bgnd);
this->setPalette(palette);
}
protected:
void resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
PaintBackground();
}
private:
QPixmap* bgnd_;
};
If I create this widget as an independent object with no parent, then it will render fine. I see the background and the child widget. If I create this widget as a child of another widget, then I see the lowest-level child, but the background is empty.
#include <QMainWindow>
#include "panel.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0) :
QMainWindow(parent)
{
Panel* solo = new Panel();
solo->show();
Panel* child = new Panel(this);
}
};
The above class instantiates the widget in 2 ways: solo gives me a dialogue with everything looking perfect. child lets me see Panel's child widgets, but the background is white.
Troubleshooting details
I thought this could be a bug in Qt as described here so I tried filtering out ThemeChange events by reimplementing the following in both Panel and MainWindow.
bool event(QEvent *event) override
{
if (event->type() != QEvent::ThemeChange)
{
return QWidget::event(event);
}
return true;
}
That didn't help.
Instead of painting my background with a QPalette in resizeEvent I found that the answer was to paint it with QPainter in paintEvent.
#include <QWidget>
#include <QPixmap>
#include <QPainter>
#include <QLabel>
class Panel : public QWidget
{
Q_OBJECT
public:
explicit Panel(QWidget *parent = 0) : QWidget(parent)
{
bgnd_ = new QPixmap(":/path/to/image.png");
QLabel* lbl = new QLabel("Hello",this);
}
protected:
void paintEvent( QPaintEvent* e )
{
QPainter painter( this );
painter.drawPixmap( 0, 0, bgnd_->scaled(size(), Qt::IgnoreAspectRatio));
QWidget::paintEvent( e );
}
private:
QPixmap* bgnd_;
};

Why does mousePressEvent to QGraphicsItem bugs out after opening a QDialog with exec or open?

I wrote a minimal working example of the problem and I believe it might be a Qt bug. But just in case I wanted to ask.
Here are My classes:
mydialog.h
#include <QDialog>
#include <QVBoxLayout>
#include <QLabel>
class MyDialog : public QDialog
{
public:
MyDialog(QWidget *parent = 0);
};
mydialog.cpp
#include "mydialog.h"
MyDialog::MyDialog(QWidget *parent):QDialog(parent)
{
QLabel *label = new QLabel("Some random dialog",this);
QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(label);
this->setLayout(layout);
}
myitem.h
#include <QGraphicsTextItem>
#include <QPainter>
#include <QDebug>
#include "mydialog.h"
class MyItem : public QGraphicsItem
{
public:
MyItem();
void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0);
QRectF boundingRect() const {return boundingBox;}
void setMyDialog(MyDialog *d){ dialog = d; }
private:
QRectF boundingBox;
MyDialog *dialog;
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *e);
};
myitem.cpp
MyItem::MyItem()
{
boundingBox = QRectF(0,0,200,100);
}
void MyItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){
painter->setBrush(QBrush(Qt::red));
painter->drawRect(boundingBox);
}
void MyItem::mousePressEvent(QGraphicsSceneMouseEvent *e){
//dialog->exec(); // BUG
//dialog->open(); // BUG
dialog->show(); // WORKS!
}
test.h
#include "myitem.h"
namespace Ui {
class Test;
}
class Test : public QMainWindow
{
Q_OBJECT
public:
explicit Test(QWidget *parent = 0);
~Test();
protected:
void resizeEvent(QResizeEvent *e);
private:
Ui::Test *ui;
MyDialog *diag;
};
And test.cpp
Test::Test(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Test)
{
ui->setupUi(this);
ui->graphicsView->setScene(new QGraphicsScene(this));
diag = new MyDialog(this);
}
void Test::resizeEvent(QResizeEvent *e){
ui->graphicsView->setSceneRect(0,0,ui->graphicsView->width(),ui->graphicsView->height());
ui->graphicsView->scene()->clear();
MyItem *item = new MyItem();
item->setMyDialog(diag);
ui->graphicsView->scene()->addItem(item);
}
Test::~Test()
{
delete ui;
}
So here is what happens (tested on Qt 5.7 and Qt 5.6). If the dialog is opened with either exec or open then, after it is closed ALL further mouse clicks ANYWHERE on the screen will open up the dialog again, making it impossible to interact with anything else drawn in there. This happens ONLY after it is opened for the first time. If i resize the screen, the item is recreated and I can click normally again. If I again click on the red box, then again all further clicks anywhere on the screen open up the dialog
However if the Dialog is opened by show, then it works as expected, only showing again if I click on the red rectangle.
Now the obvious problem is that exec make the dialog block execution until it is closed, but show doesn't. I can program around this using signals, but my question is why? and Is this a bug?
It seems that MyItem's reimplementation of mousePressEvent needs some behavior provided by default implementation. Here is the code, works fine in my machine:
void MyItem::mousePressEvent(QGraphicsSceneMouseEvent *event){
dialog->exec(); // WORKS
//dialog->open(); // WORKS
//dialog->show(); // WORKS
QGraphicsItem::mousePressEvent(event);
}

QImage and Threads

I am having problems with QImages and Qthreads.
I am trying to load big images in a Thread and then display them as QPixmap on a QLabel.
My problem is that as long as I don't use a different thread to load the QImages, everything is perfect but as soon as I use a different thread, nothing is renderder.
Though I still have a valid size for my QImage.
The thing that puzzles me is that, if I just comment the 22nd line in the cpp that moves the loader to the other thread, the label displays nicely.
Does anyone have an idea?
Here is my very simplified code:
Header :
class Loader : public QObject
{
Q_OBJECT
public:
explicit Loader(QObject *parent = 0);
signals:
void imageLoaded(QString, const QImage &);
public slots:
void loadImage(const QString& fichier);
};
namespace Ui {
class MainWindow;
}
class LoaderImages;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
signals:
void loadImage(const QString& dossier);
private slots:
void imageAvailable(const QString& dossier, const QImage& img);
private:
Ui::MainWindow *ui;
//QString mDossier;
Loader* mLoader;
//QMap<QString, QImage*> mMapDesImages;
int mWidth;
};
cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFile>
#include <QPixmap>
#include <QImage>
#include <QDir>
#include <QThread>
#include <QDebug>
#include <QLabel>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
mLoader(new Loader(NULL)),
mWidth(0)
{
ui->setupUi(this);
QThread* thread = new QThread(this);
mLoader->moveToThread(thread);
thread->start();
connect(this, SIGNAL(loadImage(QString)), mLoader, SLOT(loadImage(QString)));
connect(mLoader, SIGNAL(imageLoaded(QString,QImage)), this, SLOT(imageAvailable(QString,QImage)));
emit loadImage("C:/img.jpg");
}
void MainWindow::imageAvailable(const QString &dossier, const QImage& img)
{
mWidth += (img.width() + 20);
ui->mScrollContent->setMinimumSize(mWidth,img.height());
QLabel* lab = new QLabel(ui->mScrollContent);
lab->setFixedSize(img.width(), img.height());
lab->setGeometry(mWidth - img.width() + 20, 0, img.width(), img.height());
lab->setPixmap(QPixmap::fromImage(img));
}
MainWindow::~MainWindow()
{
delete mLoader;
delete ui;
}
Loader::Loader(QObject *parent) :
QObject(parent)
{
}
void Loader::loadImage(const QString& fichier)
{
QImage* image = new QImage(fichier);
emit imageLoaded(fichier, *image);
}
Thx!
There are several mistakes:
You're not showing the label. When the image loader is in the GUI thread, the image is loaded and the label added to the contents pane before the main window is shown. Since the parent is shown, the children become visible.
When the loading is done in another thread, you'll be adding image labels to a widget that's already shown. Such child widgets are not visible unless you explicitly show() them.
You're leaking the image in loadImage. There's no reason to put that QImage on the heap.
You're allowing a running QThread to be destructed. That's a common error since QThread is essentially broken by design. Sane C++ classes should be always destructible. QThread isn't. Thus you need a workaround.
You're not setting the minimum height of the contents widget as well.
You might wish to consider the use QtConcurrent::run instead of a dedicated thread. This is especially worthwhile when the operation you're undertaking is a one liner, more or less. I've shown both, the implementations are alternated between at runtime. Note that you need to add the concurrent module and CONFIG += c++11 to the project file.
Style bugs:
There's no reason to pass NULL for default-valued parameters that are already zero.
There's no reason to keep QObject members that have the lifetime of the parent object on the heap, if such members are constructed along with the parent object.
Just because Qt Creator comes with silly template files doesn't mean that you shouldn't be using a std::unique_ptr or QScopedPointer to hold the ui member. Naked pointers should almost never be members unless they're pointers to QObjects with parents.
As quite a bit of the code is missing, I can't really tell what else might be wrong. Below is a complete example.
// https://github.com/KubaO/stackoverflown/tree/master/questions/image-loader-24853687
#include <QtWidgets>
#include <QtConcurrent>
class Thread final : public QThread {
public:
~Thread() { quit(); wait(); }
};
class Loader : public QObject
{
Q_OBJECT
public:
explicit Loader(QObject *parent = nullptr) : QObject(parent) {}
Q_SIGNAL void imageLoaded(const QString &, const QImage &);
Q_SLOT void loadImage(const QString& fichier) {
QImage img(fichier);
if (! img.isNull()) emit imageLoaded(fichier, img);
}
};
class MainWindow : public QWidget
{
Q_OBJECT
Loader m_loader;
Thread m_loaderThread;
QGridLayout m_layout{this};
QPushButton m_open{"Open"};
QScrollArea m_view;
QWidget m_content;
int m_width{};
bool m_threadImpl = true;
Q_SIGNAL void loadImage(const QString &);
Q_SIGNAL void imageLoaded(const QString &, const QImage & img);
Q_SLOT void imageAvailable(const QString &, const QImage & img) {
int spacing = 20;
if (m_width) m_width += spacing;
auto lab = new QLabel(&m_content);
lab->setFixedSize(img.width(), img.height());
lab->setGeometry(m_width, 0, img.width(), img.height());
lab->setPixmap(QPixmap::fromImage(img));
lab->show();
m_width += img.width();
m_content.setMinimumWidth(m_width);
m_content.setMinimumHeight(qMax(m_content.minimumHeight(), img.height()));
}
Q_SLOT void open() {
auto dialog = new QFileDialog(this);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->show();
if (m_threadImpl)
connect(dialog, &QFileDialog::fileSelected, this, &MainWindow::loadImage);
else
connect(dialog, &QFileDialog::fileSelected, [this](const QString & fichier){
QtConcurrent::run([this, fichier]{
QImage img(fichier);
if (! img.isNull()) emit this->imageLoaded(fichier, img);
});
});
m_threadImpl = !m_threadImpl;
}
public:
explicit MainWindow(QWidget *parent = nullptr) : QWidget(parent) {
m_layout.addWidget(&m_open);
m_layout.addWidget(&m_view);
m_view.setWidget(&m_content);
m_loader.moveToThread(&m_loaderThread);
m_loaderThread.start();
connect(&m_open, &QPushButton::clicked, this, &MainWindow::open);
connect(this, &MainWindow::loadImage, &m_loader, &Loader::loadImage);
connect(this, &MainWindow::imageLoaded, this, &MainWindow::imageAvailable);
connect(&m_loader, &Loader::imageLoaded, this, &MainWindow::imageAvailable);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
#include "main.moc"

Movable QRubberband from one point to another

I have drawn a QRubberband on QLabel. i can resize it using QSizeGrip. Now I want to move it from one point to another using QMouseevents. Is there any one who can help me out.
void CropImage::mousePressEvent(QMouseEvent *event)
{
QLabel::mousePressEvent(event);
lastPoint = event->pos();
rubberband = new QRubberBand(QRubberBand::Rectangle,this);
rubberband->setGeometry(QRect(lastPoint, QSize()));
rubberband->show();
}
void CropImage::mouseReleaseEvent(QMouseEvent *event)
{
newPoint = event->pos();
}
this is my subclass part which is used for mouse events. the code is as following:
Resizable_rubber_band::Resizable_rubber_band(QWidget *parent) : QWidget(parent)
{
//tell QSizeGrip to resize this widget instead of top-level window
setWindowFlags(Qt::SubWindow);
QHBoxLayout* layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
QSizeGrip* grip1 = new QSizeGrip(this);
QSizeGrip* grip2 = new QSizeGrip(this);
layout->addWidget(grip1, 0, Qt::AlignLeft | Qt::AlignTop);
layout->addWidget(grip2, 0, Qt::AlignRight | Qt::AlignBottom);
rubberband = new QRubberBand(QRubberBand::Rectangle, this);
rubberband->move(0, 0);
rubberband->show();
}
void Resizable_rubber_band::resizeEvent(QResizeEvent *)
{
rubberband->resize(size());
}
void Resizable_rubber_band::mousePressEvent(QMouseEvent *event)
{
lastPoint = event->pos();
rubberband->childAt(lastPoint);
}
void Resizable_rubber_band::mouseReleaseEvent(QMouseEvent *event)
{
newpoint = event->pos();
int dragx=newpoint.x()-lastPoint.x();
int dragy=newpoint.y()-lastPoint.y();
band->move(0+dragx,0+dragy);
}
In this code, my problem is i am not getting the exact coordinates after dragging
thanks.
Ashish
Here is a quick example I made where you can move a QRubberBand using mouse events:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QRubberBand>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
void mousePressEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
private:
Ui::MainWindow *ui;
QRubberBand *rubberBand;
bool move_rubberband;
QPoint rubberband_offset;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMouseEvent>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
move_rubberband = false;
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
rubberBand->setGeometry(0,0,50,50);
rubberBand->show();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::mousePressEvent(QMouseEvent *e)
{
if(rubberBand->geometry().contains(e->pos()))
{
rubberband_offset = e->pos() - rubberBand->pos();
move_rubberband = true;
}
}
void MainWindow::mouseMoveEvent(QMouseEvent *e)
{
if(move_rubberband)
{
rubberBand->move(e->pos() - rubberband_offset);
}
}
void MainWindow::mouseReleaseEvent(QMouseEvent *e)
{
move_rubberband = false;
}

Resources