Qt Display button on top of image - c++

I need to display a button on top of an image. Something similar to
The background is a QPixmap/QImage and the button is a QPushbutton. I need to be able to dynamically change the image - so I am not sure if a stylesheet would be suitable for the task. I tried this, but could not get it to work.
Any solutions?

Subclass QWidget and implement paintEent where you can paint your image at the background. Set and change background image by stylesheet also possible.
Add layout with button to this widget.
There are something like this:
class WidgetWithButton
: public QWidget
{
Q_OBJECT
QImage m_bgImage;
public:
WidgetWithButton(QWidget* aParent)
: QWidget(aParent)
{
QHBoxLayout* l = new QHBoxLayout(this);
QPushButton* myButton = new QPushButton(tr("Close"));
l->addWidget( myButton, 0, Qt::AlignCenter );
}
void setImage(const QImage& aImage)
{
m_image = aImage;
update();
}
protected:
virtual void paintEvent(QPaintEvent* aPainEvent)
{
if (m_image.isValid())
{
QPainter painter(this);
painter.drawImage(rect(), m_image);
}
else
QWidget::paintEvent(aPainEvent);
}
};

Related

Error with drawing point on QLabel

I'm trying to draw on QLabel in Qt like this:
paintscene.h:
class PaintScene : public QWidget
{
Q_OBJECT
public:
PaintScene(QWidget* parent = NULL);
QVector<QLabel*> _layers;
QColor _color;
int _width;
void mousePressEvent(QMouseEvent* event);
private slots:
void updateWidth();
};
paintscene.cpp:
PaintScene::PaintScene(QWidget* parent) : QWidget(parent)
{
_width = 10;
_color = Qt::red;
QLabel* inital = new QLabel(this);
inital->setStyleSheet("QLabel { background-color : white; }");
_layers.push_back(inital);
QGridLayout* layout = new QGridLayout();
layout->addWidget(inital, 1, 1, 1, 1);
this->setLayout(layout);
}
void PaintScene::mousePressEvent(QMouseEvent *event)
{
QImage tmp = _layers.back()->pixmap()->toImage();
QPainter painter(&tmp);
QPen paintpen(_color);
paintpen.setWidth(_width);
painter.setPen(paintpen);
painter.drawPoint(event->x(), event->y());
_layers.back()->setPixmap(QPixmap::fromImage(tmp));
}
The list is needed because I want to implement the work with layers (QLabel - a separate layer).
However, I get an error, the program terminates. The error occurs on the line QImage tmp = _layers.back()->pixmap()->toImage();.
What makes this happen? How can this be fixed? Maybe for a layer to use something different, not QLabel?
#Jeremy Friesner is right about the reason for the error, not having a QPixmap this will be null, in my answer I will show a possible solution
void PaintScene::mousePressEvent(QMouseEvent *event)
{
QLabel *label = _layers.back();
const QPixmap *pix= label->pixmap();
QPixmap pixmap;
if(pix)
pixmap = *pix;
else{
pixmap = QPixmap(label->size());
pixmap.fill(Qt::transparent);
}
QPainter painter(&pixmap);
QPen paintpen(_color);
paintpen.setWidth(_width);
painter.setPen(paintpen);
painter.drawPoint(event->pos());
painter.end();
label->setPixmap(pixmap);
}
From the Qt docs for QLabel::pixmap():
This property holds the label's pixmap
If no pixmap has been set this will return 0.
... so when you do this:
QImage tmp = _layers.back()->pixmap()->toImage();
pixmap() is returning NULL (because the QLabel has never had any QPixmap set on it yet), and then you try to dereference that NULL pointer to call toImage() on it, hence the crash.
To avoid crashing, don't try to create a QImage from a NULL QPixmap pointer.
I suspect you wanted to be calling grab() instead of pixmap() -- grab() will create a QPixmap for you that contains the visual appearance of the QLabel. However, an even better approach would be to avoid messing about with QPixmaps at all; instead, make your own subclass of the QLabel class, and override its paintEvent(QPaintEvent *) method to first call up to QLabel::paintEvent(e) and then use a QPainter to draw the additional point afterwards. That will be easier to implement and also more efficient at runtime.

How to display image on specific coordinates in Qt?

I have this dialog window class in Qt:
class Board : public QDialog
{
Q_OBJECT
public:
explicit Board(QWidget *parent = 0);
~Board();
private:
Ui::Board *ui;
void mousePressEvent(QMouseEvent *mouseEvent);
};
I want to dislay a png image on user given coordinates in function mousePressEvent, which is called every time user clicks somewhere on the dialog window. So I need something like displayImage("path/to/image.png", coordX, coordY);. How can I do it?
The new code:
class Board : public QDialog
{
public:
Board(QWidget *parent = 0) :
QDialog(parent),
ui(new Ui::Board),
view(&scene)
{
// Set background image
/**************/
ui->setupUi(this);
QPixmap pix("path/background.png");
ui->label_board->setPixmap(pix);
/**************/
/ Set layout for displaying other images on the background
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(&view);
//or set the layout and the view in the designer if using Qt Creator
}
protected:
virtual void mousePressEvent(QMouseEvent *mouseEvent) override
{
QGraphicsPixmapItem *item = new QGraphicsPixmapItem(QPixmap("path/to/image.png"));
scene.addItem(item);
item->setPos(coordX, coordY);
}
private:
Ui::Board *ui;
QGraphicsScene scene;
QGraphicsView view;
};
The label_board is a label 500x500 set to some position with Qt Designer.
You will need to use QGraphicsView (docs) and QGraphicsScene (docs):
class Board : public QDialog
{
public:
Board(QWidget *parent = 0) :
QDialog(parent),
ui(new Ui::Board),
view(&scene)
{
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(&view);
//or set the layout and the view in the designer if using Qt Creator
//EDIT: add background like this first
QGraphicsPixmapItem *background = QGraphicsPixmapItem(QPixmap("path/to/background.png"));
scene.addItem(background);
background.setPos(0, 0); //position it to cover all the scene so at 0,0 which is the origin point
background.setScale(2.0); //scale the image to the scene rectangle to fill it
background.setZValue(-0.1); //to ensure it is always at the back
}
protected:
virtual void mousePressEvent(QMouseEvent *mouseEvent) override
{
QGraphicsPixmapItem *item = new QGraphicsPixmapItem(QPixmap("path/to/image.png"));
scene.addItem(item);
item->setPos(coordX, coordY);
}
private:
Ui::Board *ui;
QGraphicsScene scene;
QGraphicsView view;
};
And that is pretty much it. Also note that the position is that of upper left corner of the item (image). If you would want to center it you would need to adjust the position based on the proportions of the item (image).

How to create a QPushButton with the text displayed over the icon?

I am trying to create a QPushButton in my project such that the text shows on top of the custom button image or icon.
I tried the below methods:
imagePath = path;
QPixmap pixmap(imagePath);
QIcon ButtonIcon(pixmap);
button->setIcon(ButtonIcon);
button->setIconSize(pixmap.rect().size());
button->setGeometry(0,0,height,width);
button->setStyleSheet(
"background-color: gray;"
"border: 1px solid black;"
"border-radius: "+QString::number(radius)+"px;"
"color: lightGray; "
"font-size: 25px;"
);
When I try to use the setText here, it shows the icon first and text on its right. I want the text to appear on top of the icon.
I also tried the below method I found online:
imagePath = path;
button->setGeometry(0,0,height,width);
button->setStyleSheet("background-image: url(:/images/images/2adjacentTracksButton.png));"
"background-position: center center");
This one is not accepting my url path, hence is not displaying the image I need on the button.
How can I solve this?
When it comes to manipulate button, you may want to do your own class, which will implement QAbstractButton. Something like this:
class MyButton : public QAbstractButton
{
Q_OBJECT
public:
static MyButton* createButton(QIcon icon, QWidget *parent);
~MyButton();
void setText(QString);
void setIcon(eIcon);
void setOrientation(Qt::Orientation);
protected :
MyButton(QWidget *parent);
// here, you can reimplement event like mousePressEvent or paintEvent
private :
QBoxLayout* m_ButtonLayout;
QLabel* m_IconLabel;
QIcon m_Icon;
QLabel* m_TextLabel;
}
In the .cpp :
MyButton::MyButton(QWidget *parent)
: QAbstractButton(parent)
{
m_ButtonLayout = new QBoxLayout(QBoxLayout::LeftToRight, this);
m_ButtonLayout->setAlignment(Qt::AlignCenter);
m_ButtonLayout->setContentsMargins(0, 0, 0, 0);
m_ButtonLayout->setSpacing(1);
m_IconLabel = new QLabel(this);
m_IconLabel->setAlignment(Qt::AlignCenter);
m_ButtonLayout->addWidget(m_IconLabel);
m_TextLabel = new QLabel(this);
m_TextLabel->setAlignment(Qt::AlignCenter);
m_ButtonLayout->addWidget(m_TextLabel);
//m_TextLabel->hide();
}
MyButton* MyButton::createButton(QIcon icon, QWidget *parent)
{
MyButton* pButton = new MyButton(parent);
pButton->setIcon(icon);
return pButton;
}
void MyButton::setText(QString text)
{
m_TextLabel->setVisible(!text.isEmpty());
m_TextLabel->setText(text);
QAbstractButton::setText(text);
}
void MyButton::setIcon(QIcon icon)
{
m_Icon = icon;
m_IconLabel->setVisible(true);
}
void MyButton::setOrientation(Qt::Orientation orientation)
{
if (orientation == Qt::Horizontal)
m_ButtonLayout->setDirection(QBoxLayout::LeftToRight);
else
m_ButtonLayout->setDirection(QBoxLayout::TopToBottom);
}
And now you can create your button with your icon by calling the static method:
MyButton* button = MyButton::createButton(myButtonIcon, this);
It is just a basic example I gave you, and I am not sure it will work (this is a thing I did some time ago) but you can give it a shot. Hope that helps !

How can I draw a selection rectangle on QScrollArea?

I have created an Image Viewer application that opens and saves an image and load the image on QLabel,then I created a ScrollArea to scroll for large images, in my second step I am trying to draw a selection rectangle to select a specific sub area , the steps I have taken to paint the selection rectangle as follows:
1- I used an PaintEvent to paint the rectangle.
2- I used MouseEvent to select the sub area.
The problem is, when I run my code, I can draw the rectangle on the QWidget but not on the ScrollArea.
Here is my code:
in imageviewer.h
class ImageViewer : public QWidget{
Q_OBJECT
public:
explicit ImageViewer(QWidget *parent = 0);
~ImageViewer();
private:
Ui::ImageViewer *ui;
private slots:
void on_openButton_pressed();
void on_saveButton_pressed();
private:
QPixmap image;
QImage *imageObject;
bool selectionStarted;
QRect selectionRect;
QMenu contextMenu;
protected:
void paintEvent(QPaintEvent *e);
void mousePressEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
};
this is imageviewer.cpp
ImageViewer::ImageViewer(QWidget *parent) :
QWidget(parent),
ui(new Ui::ImageViewer)
{
ui->setupUi(this);
ui->scrollArea->setWidget(ui->imageLabel);
}
open & save functions:
void ImageViewer::on_openButton_pressed()
{
QString imagePath = QFileDialog::getOpenFileName(this, tr("Open File") , "" ,
tr("JPEG (*.jpg *.jpeg);;PNG (*.png);;BMP (*.bmp)"));
imageObject = new QImage();
imageObject->load(imagePath);
image = QPixmap::fromImage(*imageObject);
ui->imageLabel->setPixmap(image);
ui->imageLabel->adjustSize();
}
void ImageViewer::on_saveButton_pressed()
{
QString imagePath = QFileDialog::getSaveFileName(this, tr("Save File") , "" ,
tr("JPEG (*.jpg *.jpeg);;PNG (*.png);;BMP (*.bmp)"));
*imageObject = image.toImage();
imageObject->save(imagePath);
}
paint & mouse event functions:
void ImageViewer::paintEvent(QPaintEvent *e){
QWidget::paintEvent(e);
QPainter painter(this);
painter.setPen(QPen(QBrush(QColor(0,0,0,180)),1,Qt::DashLine));
painter.setBrush(QBrush(QColor(255,255,255,120)));
painter.drawRect(selectionRect);
}
void ImageViewer::mousePressEvent(QMouseEvent *e){
if(e->button() == Qt::RightButton){
if(selectionRect.contains(e->pos()))
contextMenu.exec(this->mapToGlobal(e->pos()));
}
else{
selectionStarted = true;
selectionRect.setTopLeft(e->pos());
selectionRect.setBottomRight(e->pos());
}
}
void ImageViewer::mouseMoveEvent(QMouseEvent *e){
if(selectionStarted){
selectionRect.setBottomRight(e->pos());
repaint();
}
}
void ImageViewer::mouseReleaseEvent(QMouseEvent *e){
selectionStarted = false;
}
and this is a screenshot for my application
How can I draw a selection rectangle on QScrollArea?
You need QRubberBand which supposed to be directly applied to ui->imageLabel widget you use to display an image you want to scroll. Whether or not you should overload QLabel for ui->imageLabel is a question of implementation. Please see the example for mouse events which, of course better be applied to your label image class.
I understand from the way the code looks you used Qt Designer? That complicates overriding that label class but you can either not use .ui file or use event filter for trapping all the events for the given object.
What you currently have can be finally polished enough to be good for actual use, of course. It just makes much more effort for you. You can avoid overloading paintEvent etc. and the key is to apply the right approach directly where it needs to be applied.
// the example applied to authors code as requested
class MyImageLabel : public QLabel
{
public:
explicit MyImageLabel(QWidget *parent = 0) : QLabel(parent), rubberBand(0) {}
private:
QRubberBand* rubberBand;
QPoint origin;
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event)
};
void MyImageLabel::mousePressEvent(QMouseEvent *event)
{
origin = event->pos();
if (!rubberBand)
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
rubberBand->setGeometry(QRect(origin, QSize()));
rubberBand->show();
}
void MyImageLabel::mouseMoveEvent(QMouseEvent *event)
{
rubberBand->setGeometry(QRect(origin, event->pos()).normalized());
}
void MyImageLabel::mouseReleaseEvent(QMouseEvent *event)
{
rubberBand->hide();
// determine selection, for example using QRect::intersects()
// and QRect::contains().
}
ImageViewer::ImageViewer(QWidget *parent) :
QWidget(parent),
ui(new Ui::ImageViewer)
{
ui->setupUi(this);
// resolve this better than:
delete ui->imageLabel; // you already have the QLabel object here?
ui->imageLabel = new MyImageLabel;
ui->scrollArea->setWidget(ui->imageLabel);
}

How can I edit the default window title and frame in a Qt application?

I want to create a personalized UI with my Qt application. For this reason I want to edit the default window frame that you can see at any type of window application.
The application that contains the default window title and frame:
http://0000.2.img98.net/out.php/i12977_with-default-title-and-frame.jpg
The application that edit the window title and frame:
http://0000.2.img98.net/out.php/i12978_without-defualt-title-andframe.jpg
You need to create a new class derived from QWidget, and pass Qt::FramelessWindowHint argument to QWidget constructor, like this:
class MyWidget : public QWidget {
public:
MyWidget(QWidget* parent) : QWidget(parent, Qt::FramelessWindowHint) {...}
After it you need to reimplement QWidget::paintEvent (QPaintEvent * event) and draw any design you want.
For example, you have main window design as a PNG image.
class MyWidget : public QWidget {
Q_OBJECT
private:
QPushButton* button;
QLabel* label;
QComboBox* combobox;
QPixmap pixmap;
public:
explicit MyWidget(QWidget *parent = 0) : QWidget(parent, Qt::FramelessWindowHint) {
// Create some controls
button = new QPushButton();
label = new QLabel();
combobox = new QComboBox();
QVBoxLayout* l = new QVBoxLayout();
l->addWidget(button);
l->addWidget(label);
l->addWidget(combobox);
setLayout(l);
resize (500, 500);
setAttribute(Qt::WA_TranslucentBackground); // enable translucent background
pixmap = QPixmap("./1.png"); // load design picture
};
protected:
virtual void paintEvent (QPaintEvent* event) {
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(0, 0, 0, 0));
painter.drawRect(this->rect());
painter.drawPixmap(this->rect(), pixmap, pixmap.rect());
};
Since we don't see a titlebar, we need to implement window drag operation:
private:
bool pressed;
QPoint mousePressPoint;
protected:
virtual void mousePressEvent ( QMouseEvent * event ) {
QWidget::mousePressEvent(event);
if (!pressed) {
pressed = true;
mousePressPoint = event->pos();
}
}
virtual void mouseMoveEvent ( QMouseEvent * event ) {
QWidget::mouseMoveEvent(event);
if (pressed) {move(event->globalPos() - mousePressPoint);}
}
virtual void mouseReleaseEvent ( QMouseEvent * event ) {
QWidget::mouseReleaseEvent(event);
if (pressed) {pressed = false;}
}