My goal
This is an example of how I want the QPixmap to be contained inside the QLabel. The image keeps its aspect ratio and fills the given dimension. It will be clipped to fit.
Here is the CSS equivalent:
object-fit: cover;
Current code
This almost does what I want, but it stretches the image to cover the QLabel, so the aspect ratio isn't reserved.
QPixmap *img = new QPixmap("image.png");
ui->label->setPixmap(*img);
ui->label->setScaledContents(true);
delete img;
Can be implemented as QWidget subclass using QTransform.
class Label : public QWidget
{
Q_OBJECT
public:
explicit Label(QWidget *parent = nullptr);
void setPixmap(const QPixmap& image);
protected:
void paintEvent(QPaintEvent *event);
QPixmap mPixmap;
};
Label::Label(QWidget *parent) : QWidget(parent) {
}
void Label::setPixmap(const QPixmap &pixmap)
{
mPixmap = pixmap;
update();
}
void Label::paintEvent(QPaintEvent *event)
{
if (mPixmap.isNull()) {
return;
}
double width = this->width();
double height = this->height();
double pixmapWidth = mPixmap.width();
double pixmapHeight = mPixmap.height();
double scale = qMax(width / pixmapWidth, height / pixmapHeight);
QTransform transform;
transform.translate(width / 2, height / 2);
transform.scale(scale, scale);
transform.translate(-pixmapWidth / 2, -pixmapHeight / 2);
QPainter painter(this);
painter.setTransform(transform);
painter.drawPixmap(QPoint(0,0), mPixmap);
}
Related
How i could create a click effect similar to push buttons on a QLabel?
QPixmap pixmap;
pixmap.load(":/files/hotkeys.png");
int w = 131;
int h = 71;
pixmap = pixmap.scaled(w, h, Qt::KeepAspectRatio);
// Label
ui.label->setGeometry(220, 220, w, h);
ui.label->setPixmap(pixmap);
// Button
QIcon icon(pixmap);
ui.toolButton->setIconSize(QSize(w, h));
ui.toolButton->setIcon(icon);
ui.toolButton->setStyleSheet("QToolButton { background-color: transparent }");
By click effect i mean like when you click on a push button containing a picture:
Ideally you'd just use a QToolButton or QPushButton, but if you must use a QLabel, you could do it by subclassing QLabel with a custom paintEvent() to give the desired effect, something like this:
class MyLabel : public QLabel
{
public:
MyLabel(const QPixmap & pm) : _isMouseDown(false) {setPixmap(pm);}
virtual void mousePressEvent( QMouseEvent * e) {_isMouseDown = true; update(); e->accept();}
virtual void mouseReleaseEvent(QMouseEvent * e) {_isMouseDown = false; update(); e->accept();}
virtual void paintEvent(QPaintEvent * e)
{
QPainter p(this);
const int offset = _isMouseDown ? 2 : 0;
p.drawPixmap(QPoint(offset, offset), *pixmap());
}
private:
bool _isMouseDown;
};
I am trying to achieve rubber band rectangle zoom in. But here I need to zoom in only the part which comes under rubber band rectangle. I do not want to zoom in the whole view.
For that I was told to create rubber band rectangle whose point I will know. And then use
void QGraphicsView::ensureVisible(qreal x, qreal y, qreal w, qreal h, int xmargin = 50, int ymargin = 50)
or
void QGraphicsView::centerOn(qreal x, qreal y)
So just to understand, what this ensureVisible() does, I called it with some points which are present in my design. But just a pop up windows appeared but nothing was there to display. So I left that task and concentrated on, how to get rubber band rectagle points. So I tried to add some mouse events in my existing sample project but I got following error.
schematicdesign.cpp:75:47: error: non-static member 'mapToScene' found
in multiple base-class subobjects of type 'QGraphicsItem': class
SchematicDesign -> class QGraphicsRectItem -> class
QAbstractGraphicsShapeItem -> class QGraphicsItem class
SchematicDesign -> class QGraphicsPathItem -> class
QAbstractGraphicsShapeItem -> class QGraphicsItem
qgraphicsitem.h:360:13: note: member found by ambiguous name lookup
Such error is not only for mapToScene() but also for scene() and transform().
What I understood from the error is :
I have inherited schematicDesign class from multiple classes (
QGraphicsRectItem and QGraphicsPathItem ) so when it tries to find the
definition of mapToScene() or scene() or transform(), it finds in
multiple classes so gets confused in which one to pick. ( please
correct if I am wrong )
Here is my code:
Widget.h
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QGraphicsView
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_schematicButoon_clicked();
private:
Ui::Widget *ui;
QGraphicsScene* scene;
QGraphicsView* view;
};
Widget.cpp
Widget::Widget(QWidget *parent)
: QGraphicsView(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
scene = new QGraphicsScene(this);
view = new QGraphicsView(this);
view->setScene(scene);
ui->verticalLayout_2->addWidget(view);
}
void Widget::on_schematicButoon_clicked()
{
SchematicDesign* sd1 = new SchematicDesign();
QGraphicsRectItem* clkPin = sd1->createRect(20,160,20,20);
scene->addItem(clkPin);
// some more createRect() method
QPen mPen;
mPen.setWidth(3);
mPen.setBrush(Qt::yellow);
QPolygonF clknet;
clknet<< QPointF (40,170) << QPointF (186,170);
QGraphicsPathItem* clk = sd1->drawPolyline(clknet);
scene->addItem(clk);
// some more drawPolyline() method.
QGraphicsPathItem* text = sd1->addText(i1Instance,275.0,138.0,"I1");
scene->addItem(text);
// some more addText() method
}
SchematicDesign.h
class SchematicDesign : public QGraphicsRectItem,QGraphicsPathItem
{
public:
explicit SchematicDesign();
explicit SchematicDesign(qreal x, qreal y, qreal width, qreal height,QGraphicsItem *parent = nullptr)
: QGraphicsRectItem(x, y, width, height, parent)
{}
explicit SchematicDesign(QPainterPath pPath);
signals:
public:
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
QGraphicsRectItem* createRect(qreal x, qreal y, qreal width, qreal height);
QGraphicsPathItem* drawPolyline(QPolygonF poly);
QGraphicsPathItem* addText(QGraphicsRectItem *rect, double d1, double d2, QString s);
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
private:
QPointF selectTopLeft;
bool drawingSelection;
QGraphicsRectItem* lastRect;
public:
QPoint rubberBandOrigin;
bool rubberBandActive;
};
#endif // SCHEMATICDESIGN_H
SchematicDesign.cpp
SchematicDesign::SchematicDesign(){}
SchematicDesign::SchematicDesign(QPainterPath pPath)
{}
void SchematicDesign::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
auto copied_option = *option;
copied_option.state &= ~QStyle::State_Selected;
auto selected = option->state & QStyle::State_Selected;
QGraphicsRectItem::paint(painter, &copied_option, widget);
QGraphicsPathItem::paint(painter, &copied_option, widget);
if (selected)
{
painter->save();
painter->setBrush(Qt::NoBrush);
painter->setPen(QPen(option->palette.windowText(), 0, Qt::SolidLine));
painter->drawPath(QGraphicsRectItem::shape());
painter->drawPath(QGraphicsPathItem::shape());
painter->restore();
}
}
QGraphicsRectItem *SchematicDesign::createRect(qreal x, qreal y, qreal width, qreal height)
{...... }
QGraphicsPathItem* SchematicDesign::drawPolyline(QPolygonF poly)
{....}
QGraphicsPathItem* SchematicDesign::addText(QGraphicsRectItem *rect, double d1, double d2, QString s)
{.....}
void SchematicDesign::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsItem * sceneItem = scene()->itemAt(mapToScene(event->pos()),transform()); //error
if(!sceneItem){
selectTopLeft = event->pos();
event->pos();
drawingSelection = true;
}
QGraphicsRectItem::mousePressEvent(event);
}
void SchematicDesign::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if(drawingSelection){
QGraphicsItem * itemToRemove = lastRect;
if(itemToRemove){
fitInView(itemToRemove,Qt::KeepAspectRatio); //error at fitInView : Use of undeclared identifier
scene()->removeItem(itemToRemove); //error at scene()
delete itemToRemove;
lastRect = nullptr;
}
}
drawingSelection = false;
QGraphicsRectItem::mouseReleaseEvent(event);
}
void SchematicDesign::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if(drawingSelection){
//Selection region
int x = selectTopLeft.rx();
int y = selectTopLeft.ry();
int h = event->pos().rx();
int w = event->pos().ry();
//QRect selectRegion = QRect(selectTopLeft,event->pos());
QRect selectRegion = QRect(x,y,h,w);
QPainterPath path;
path.addRect(selectRegion);
scene()->setSelectionArea(mapToScene(path)); //error at scene()
//Draw visual feedback for the user
QGraphicsItem * itemToRemove = lastRect;
scene()->removeItem(itemToRemove); // error at scene()
lastRect = scene()->addRect(QRectF(mapToScene(selectTopLeft),
mapToScene(event->pos())).normalized()); // error at scene() and mapToScene()
lastRect->setBrush(QBrush(QColor(255, 0, 0, 50)));
delete itemToRemove;
}
QGraphicsRectItem::mouseMoveEvent(event);
}
How to tackle such type of error ? Should not I inherit my class from multiple classes ?
I am trying to add a glow effect to a QLabel so that it looks like the time display in the following picture:
I found out that you can "misuse" a QGraphicsDropShadowEffect for this:
QGraphicsDropShadowEffect * dse = new QGraphicsDropShadowEffect();
dse->setBlurRadius(10);
dse->setOffset(0);
dse->setColor(QColor(255, 255, 255));
ui.label->setGraphicsEffect(dse);
However, the resulting effect is too weak, you can barely see it:
Unfortunately, you can not modify the strength of the effect, only color and blur radius.
One idea would be to apply multiple QGraphicsDropShadowEffect to the label, so that it gets more visible due to overlapping. But calling ui.label->setGraphicsEffect(dse); will always delete any previous effects, i.e. I was not able to set multiple QGraphicsEffect to the same object.
Any ideas how you can create a clearly visible glow effect with Qt?
Meanwhile, I tinkered my own graphics effect based on QGraphicsBlurEffect and using parts of this answer. If you know any better solutions, let me know.
qgraphicsgloweffect.h:
#pragma once
#include <QGraphicsEffect>
#include <QGraphicsBlurEffect>
#include <QGraphicsColorizeEffect>
#include <QGraphicsPixmapItem>
#include <QGraphicsScene>
#include <QPainter>
class QGraphicsGlowEffect :
public QGraphicsEffect
{
public:
explicit QGraphicsGlowEffect(QObject *parent = 0);
QRectF boundingRectFor(const QRectF &rect) const;
void setColor(QColor value);
void setStrength(int value);
void setBlurRadius(qreal value);
QColor color() const;
int strength() const;
qreal blurRadius() const;
protected:
void draw(QPainter* painter);
private:
static QPixmap applyEffectToPixmap(QPixmap src, QGraphicsEffect *effect, int extent);
int _extent = 5;
QColor _color = QColor(255, 255, 255);
int _strength = 3;
qreal _blurRadius = 5.0;
};
qgraphicsgloweffect.cpp:
#include "QGraphicsGlowEffect.h"
#include <QtCore\qmath.h>
QGraphicsGlowEffect::QGraphicsGlowEffect(QObject *parent) : QGraphicsEffect(parent)
{
}
void QGraphicsGlowEffect::setColor(QColor value) {
_color = value;
}
void QGraphicsGlowEffect::setStrength(int value) {
_strength = value;
}
void QGraphicsGlowEffect::setBlurRadius(qreal value) {
_blurRadius = value;
_extent = qCeil(value);
updateBoundingRect();
}
QColor QGraphicsGlowEffect::color() const {
return _color;
}
int QGraphicsGlowEffect::strength() const {
return _strength;
}
qreal QGraphicsGlowEffect::blurRadius() const {
return _blurRadius;
}
QRectF QGraphicsGlowEffect::boundingRectFor(const QRectF &rect) const {
return QRect(
rect.left() - _extent,
rect.top() - _extent,
rect.width() + 2 * _extent,
rect.height() + 2 * _extent);
}
void QGraphicsGlowEffect::draw(QPainter* painter) {
QPoint offset;
QPixmap source = sourcePixmap(Qt::LogicalCoordinates, &offset);
QPixmap glow;
QGraphicsColorizeEffect *colorize = new QGraphicsColorizeEffect;
colorize->setColor(_color);
colorize->setStrength(1);
glow = applyEffectToPixmap(source, colorize, 0);
QGraphicsBlurEffect *blur = new QGraphicsBlurEffect;
blur->setBlurRadius(_blurRadius);
glow = applyEffectToPixmap(glow, blur, _extent);
for (int i = 0; i < _strength; i++)
painter->drawPixmap(offset - QPoint(_extent, _extent), glow);
drawSource(painter);
}
QPixmap QGraphicsGlowEffect::applyEffectToPixmap(
QPixmap src, QGraphicsEffect *effect, int extent)
{
if (src.isNull()) return QPixmap();
if (!effect) return src;
QGraphicsScene scene;
QGraphicsPixmapItem item;
item.setPixmap(src);
item.setGraphicsEffect(effect);
scene.addItem(&item);
QSize size = src.size() + QSize(extent * 2, extent * 2);
QPixmap res(size.width(), size.height());
res.fill(Qt::transparent);
QPainter ptr(&res);
scene.render(&ptr, QRectF(), QRectF(-extent, -extent, size.width(), size.height()));
return res;
}
Then you can use it like:
QGraphicsGlowEffect * glow = new QGraphicsGlowEffect();
glow->setStrength(4);
glow->setBlurRadius(7);
ui.label->setGraphicsEffect(glow);
This results in a nice glow effect:
I tried to find some helps around internet, i had some codes to test but none works like i want.
I would draw with Qt something pixel by pixel.
I tried with a QImage within a QLabel with the protected event mousePressEvent it worked, but the pixels are too small to see them.
I tried to scale my Image, it's much better, but the position X, Y are not the same position of the pixels now or they're not synchronized with the pixels locations.
Here a screenshot of what i want do, if someone has an idea to recreate this...
If i can exactly done this via Qt i'll save a lots of time, just a basic drawing pixel by pixel, i created this example with Paint, Black and White format zoomed to 800%.
Thanks.
This is a solution hard-coded for a specific size - just implement relative coordinate mapping between pixmap and draw widget and you are set.
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0) : QWidget(parent), pressed(0) {
color = Qt::black;
pixmap = new QPixmap("h:/small.png");
resize(240, 240);
}
~Widget() { if (pixmap) delete pixmap; }
protected:
void paintEvent(QPaintEvent *) {
QPainter painter(this);
painter.drawPixmap(0, 0, pixmap->scaled(240, 240));
painter.drawPixmap(0, 0, *pixmap);
}
void mousePressEvent(QMouseEvent *e) {
if (e->button() == Qt::RightButton)
color = color == Qt::black ? Qt::white : Qt::black;
else {
pressed = 1;
draw(e);
}
}
void mouseReleaseEvent(QMouseEvent *) { pressed = 0; }
void mouseMoveEvent(QMouseEvent *e) { draw(e); }
private:
void draw(QMouseEvent *e) {
if (pressed) {
QPainter painter(pixmap);
painter.setPen(color);
int x = e->pos().x() / 12;
int y = e->pos().y() / 12;
painter.drawPoint(x, y);
repaint();
}
}
QColor color;
QPixmap *pixmap;
bool pressed;
};
I want to be able to use rubberband to select an area of an image, then remove the parts of the image outside of the rubberband and display the new image. However when I currently do it, it doesnt crop the correct areas and gives me the wrong image.
#include "mainresizewindow.h"
#include "ui_mainresizewindow.h"
QString fileName;
MainResizeWindow::MainResizeWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainResizeWindow)
{
ui->setupUi(this);
ui->imageLabel->setScaledContents(true);
connect(ui->actionOpen, SIGNAL(triggered()), this, SLOT(open()));
}
MainResizeWindow::~MainResizeWindow()
{
delete ui;
}
void MainResizeWindow::open()
{
fileName = QFileDialog::getOpenFileName(this, tr("Open File"), QDir::currentPath());
if (!fileName.isEmpty()) {
QImage image(fileName);
if (image.isNull()) {
QMessageBox::information(this, tr("Image Viewer"),
tr("Cannot load %1.").arg(fileName));
return;
}
ui->imageLabel->setPixmap(QPixmap::fromImage(image));
ui->imageLabel->repaint();
}
}
void MainResizeWindow::mousePressEvent(QMouseEvent *event)
{
if(ui->imageLabel->underMouse()){
myPoint = event->pos();
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
rubberBand->show();
}
}
void MainResizeWindow::mouseMoveEvent(QMouseEvent *event)
{
rubberBand->setGeometry(QRect(myPoint, event->pos()).normalized());
}
void MainResizeWindow::mouseReleaseEvent(QMouseEvent *event)
{
QRect myRect(myPoint, event->pos());
rubberBand->hide();
QPixmap OriginalPix(*ui->imageLabel->pixmap());
QImage newImage;
newImage = OriginalPix.toImage();
QImage copyImage;
copyImage = copyImage.copy(myRect);
ui->imageLabel->setPixmap(QPixmap::fromImage(copyImage));
ui->imageLabel->repaint();
}
Any help appreciated.
There are two issues here - the position of the rect relative to the image and the fact that the image is (potentially) scaled in the label.
Position issue:
QRect myRect(myPoint, event->pos());
You should perhaps change this to:
QPoint a = mapToGlobal(myPoint);
QPoint b = event->globalPos();
a = ui->imageLabel->mapFromGlobal(a);
b = ui->imageLabel->mapFromGlobal(b);
Then, the label may be scaling the image because you used setScaledContents(). So you need to work out the actual coordinates on the unscaled image. Something like this maybe (untested/compiled):
QPixmap OriginalPix(*ui->imageLabel->pixmap());
double sx = ui->imageLabel->rect().width();
double sy = ui->imageLabel->rect().height();
sx = OriginalPix.width() / sx;
sy = OriginalPix.height() / sy;
a.x = int(a.x * sx);
b.x = int(b.x * sx);
a.y = int(a.y * sy);
b.y = int(b.y * sy);
QRect myRect(a, b);
...