I use a custom QQuickPaintedItem to display a Qimage in QML.
I registered it using:
main.cpp
qmlRegisterType<ImageItem>("myextension", 1, 0, "ImageItem");
ImageItem.h
class ImageItem : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(QImage image READ image WRITE setImage NOTIFY imageChanged)
public:
ImageItem(QQuickItem *parent = nullptr);
Q_INVOKABLE void setImage(const QImage &image);
Q_INVOKABLE void resize(const int& width);
void paint(QPainter *painter);
QImage image() const;
signals:
void imageChanged();
private:
QImage current_image;
};
How can I resize QImage in QML for displaying purposes?
Didnt expect it would be that simple.
ImageItem {
image: sqlImageData
id: myIcon
width: 50 //resizing
height:50 //resizing
}
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);
}
I have a QGraphicsSvgItem subclass where I would like to modify the svg color. I want to use for that QGraphicsColorizeEffect, and it works great.
My items also have a custom selection rectangle, highlighted - similar to other item types.
When I apply the colorize effect, the highlight also turns to the same color...
I have tried to setEnabled(false); in paint but it seems to have no effect.
sample code:
file mysvg.h
#ifndef MYSVG_H
#define MYSVG_H
#include <QGraphicsSvgItem>
#include <QGraphicsColorizeEffect>
class MySvg : public QGraphicsSvgItem
{
public:
MySvg();
~MySvg();
virtual void paint(QPainter* painter,
const QStyleOptionGraphicsItem* option,
QWidget* widget = NULL);
private:
QGraphicsColorizeEffect* m_effect;
void drawSelectionRectangle(QPainter* painter, const QStyleOptionGraphicsItem* option, const QRectF& rectangle);
};
#endif // MYSVG_H
file mysvg.cpp
#include <QStyleOptionGraphicsItem>
#include <QStyle>
#include <QPainterPath>
#include <QPainter>
#include <QFileDialog>
#include <QSvgRenderer>
MySvg::MySvg()
{
m_effect = new QGraphicsColorizeEffect();
m_effect->setColor(Qt::red);
setGraphicsEffect(m_effect);
setFlags(QGraphicsItem::ItemIsMovable |
QGraphicsItem::ItemIsFocusable |
QGraphicsItem::ItemIsSelectable);
QString filename = QFileDialog::getOpenFileName(0, tr("Open Svg File"),
QString(), tr("Svg files (*.svg *.svgz)"));
setSharedRenderer(new QSvgRenderer(filename));
}
MySvg::~MySvg()
{
delete renderer();
delete m_effect;
}
void MySvg::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QStyleOptionGraphicsItem opt(*option);
opt.state = QStyle::State_None;
QGraphicsSvgItem::paint(painter, &opt, widget);
//m_effect->setEnabled(false); // no effect though seemed logical
QRectF rectangle = boundingRect();
if (option->state & (QStyle::State_Selected))
drawSelectionRectangle(painter, option, rectangle);
//m_effect->setEnabled(true);
}
void MySvg::drawSelectionRectangle(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRectF &rectangle)
{
painter->setPen(QPen(option->palette.windowText(), 0, Qt::DotLine));
painter->setBrush(QColor(255, 188, 0, 50));
painter->drawRect(rectangle);
}
file main.cpp
#include <QApplication>
#include <QGraphicsView>
#include "mysvg.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QGraphicsScene s;
QGraphicsView view;
view.setScene(&s);
s.setSceneRect(-50, -50, 500, 650);
view.show();
MySvg* svg = new MySvg();
s.addItem(svg);
return app.exec();
}
file mysvg.pro
QT += core gui svg
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = mysvg
TEMPLATE = app
SOURCES += main.cpp \
mysvg.cpp
HEADERS += mysvg.h
I have considered making a QGraphicsSvgItem a private member of the MySvg item - but the MySvg item has to do a lot of other things, and I don't know what to do with the renderer (who would own it...) - if I can figure out how to make a QGraphicsSvgItem subclass a member of the MySvg class, I can apply the colorize to the member and perform all other operations on the MySvg item...
Please help me figure a way to apply color to the svg, but not other drawing portion of the item.
Edit - I have tried to add a member item to the class and apply the colorize effect to the member - but it doesn't apply the colorize effect at all... The svg loads with all original colors.
Here is the code containing a member item:
new mysvg.h
class SvgMember : public QGraphicsSvgItem
{
public:
SvgMember (const QByteArray &content, const QColor& c);
~SvgMember ();
private:
QGraphicsColorizeEffect* m_effect;
};
class MySvg : public QGraphicsItem
{
public:
MySvg();
~MySvg();
virtual void paint(QPainter* painter,
const QStyleOptionGraphicsItem* option,
QWidget* widget = NULL);
virtual QRectF boundingRect() const;
virtual QPainterPath shape() const;
private:
void drawSelectionRectangle(QPainter* painter, const QStyleOptionGraphicsItem* option, const QRectF& rectangle);
SvgMember * m_member;
};
new mysvg.cpp
MySvg::MySvg()
{
setFlags(QGraphicsItem::ItemIsMovable |
QGraphicsItem::ItemIsFocusable |
QGraphicsItem::ItemIsSelectable);
QString filename = QFileDialog::getOpenFileName(0, QObject::tr("Open Svg File"),
QString(), QObject::tr("Svg files (*.svg *.svgz)"));
QFile f(filename);
f.open(QFile::ReadOnly | QFile::Text);
QByteArray svgContents = f.readAll();
f.close();
m_member = new SvgMember (svgContents, Qt::red);
}
MySvg::~MySvg()
{
delete m_member;
}
void MySvg::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QStyleOptionGraphicsItem opt(*option);
opt.state = QStyle::State_None;
m_member->paint(painter, &opt, widget);
QRectF rectangle = boundingRect();
if (option->state & (QStyle::State_Selected))
drawSelectionRectangle(painter, option, rectangle);
}
/*! \brief reimplemented to use member rectangle */
QRectF MySvg::boundingRect() const
{
return m_member->boundingRect();
}
/*! \brief reimplemented to use member shape */
QPainterPath MySvg::shape() const
{
return m_member->shape();
}
void MySvg::drawSelectionRectangle(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRectF &rectangle)
{
painter->setPen(QPen(option->palette.windowText(), 0, Qt::DotLine));
painter->setBrush(QColor(255, 188, 0, 50));
painter->drawRect(rectangle);
}
SvgMember ::SvgMember (const QByteArray &content, const QColor &c)
{
m_effect = new QGraphicsColorizeEffect();
setGraphicsEffect(m_effect);
m_effect->setColor(c);
setSharedRenderer(new QSvgRenderer(content));
}
SvgMember ::~SvgMember ()
{
delete renderer();
delete m_effect;
}
What can I do to apply the colorize effect to the svg - but not to the selection rectangle ?
The effect is a feature of the base class QGraphicsItem. It is applied to the entire graphics item and all its children. So, everything that is painted inside the item is affected by its effect.
The selection rectangle should be painted outside of the SVG item object.
It can be achieved by enclosing QGraphicsSvgItem by the composite class QGraphicsItemGroup.
When a QGraphicsItem is added to QGraphicsItemGroup it becomes reparented. Thus the item is destroyed when the group object is destroyed. So, it is not needed to delete it manually. QGraphicsItem takes ownership of effect, so it is not needed to delete the effect object.
The following class MyGraphicsItemGroup works as you expect.
Implementation "mygraphicsitemgroup.cpp"
#include "mygraphicsitemgroup.h"
#include <QGraphicsColorizeEffect>
#include <QGraphicsSvgItem>
#include <QStyleOptionGraphicsItem>
#include <QPainter>
#include <QFileDialog>
MyGraphicsItemGroup::MyGraphicsItemGroup()
{
setFlags(QGraphicsItem::ItemIsMovable |
QGraphicsItem::ItemIsFocusable |
QGraphicsItem::ItemIsSelectable);
QString filename = QFileDialog::getOpenFileName(0,
QObject::tr("Open Svg File"), QString(),
QObject::tr("Svg files (*.svg *.svgz)"));
QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect();
effect->setColor(Qt::red);
QGraphicsSvgItem *svg = new QGraphicsSvgItem(filename);
svg->setGraphicsEffect(effect);
addToGroup(svg);
}
void MyGraphicsItemGroup::paint(QPainter* painter,
const QStyleOptionGraphicsItem* option, QWidget* widget)
{
QStyleOptionGraphicsItem opt(*option);
opt.state = QStyle::State_None;
QGraphicsItemGroup::paint(painter, &opt, widget);
QRectF rectangle = boundingRect();
if (option->state & QStyle::State_Selected)
drawSelectionRectangle(painter, option, rectangle);
}
void MyGraphicsItemGroup::drawSelectionRectangle(QPainter *painter,
const QStyleOptionGraphicsItem *option, const QRectF &rectangle)
{
painter->setPen(QPen(option->palette.windowText(), 0, Qt::DotLine));
painter->setBrush(QColor(255, 188, 0, 50));
painter->drawRect(rectangle);
}
Header "mygraphicsitemgroup.h"
#ifndef MYGRAPHICSITEMGROUP_H
#define MYGRAPHICSITEMGROUP_H
#include <QGraphicsItemGroup>
class MyGraphicsItemGroup : public QGraphicsItemGroup
{
public:
MyGraphicsItemGroup();
virtual void paint(QPainter* painter,
const QStyleOptionGraphicsItem* option, QWidget* widget);
void drawSelectionRectangle(QPainter *painter,
const QStyleOptionGraphicsItem *option, const QRectF &rectangle);
};
#endif // MYGRAPHICSITEMGROUP_H
I have a class derived from QGraphicsItem, which is basically like this:
class MyRect: public QObject, public QGraphicsItem
{
Q_OBJECT
Q_INTERFACES(QGraphicsItem)
public:
explicit MyRect(QObject *parent = 0);
MyRect(QColor fillColor, float val, QString txt = "", bool isLeaf = false);
int width, height;
protected:
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
virtual QRectF boundingRect() const;
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
};
The problem is the mouseReleaseEvent and mousePressEvent only accept QGraphicsSceneMouseEvent argument which cannot detect right button click. I know there is a mousePressEvent(QMouseEvent *event) version that I can use, but it seems not work for QGraphicsItem....Just cannot get stuck here...I appreciate your answer.
Try re-implementing QGraphicsItem::contextMenuEvent and checking the QGraphicsSceneContextMenuEvent::Reason for if the event was caused by a mouse click.
I am attempting to spawn a custom QGraphicsItem inside my scene, but am unsure of how exactly to map the point from its origin to the Item. My location comes from a dropEvent in my MainWindow:
void MainWindow::dropEvent(QDropEvent *event)
{
QPointF dropPos = ui->GraphicsView->mapFrom(this, event->pos());
vModule *module = new vModule(dropPos);
ui->GraphicsView->scene->addItem(module);
}
This is my vModule.cpp:
vModule::vModule(QPointF dropPos)
{
QPointF pos = mapFromScene(dropPos);
setX(pos.x());
setY(pos.y());
// ...
}
// Event handler implementation, shouldn't be relevant
And my vModule.h:
class vModule : public QObject, public QGraphicsItem
{
public:
explicit vModule(QPointF dropPos);
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
QRectF boundingRect() const;
protected:
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
void mousePressEvent(QGraphicsSceneMouseEvent *event);
}
This is the closest to what I logically thought the conversion would be, but it is quite wrong in practice, more wrong than simply passing the position from the event as-is (which is a constant difference in position from the main window?). Can anyone correct my error?
In the constructor of vModule, you're calling mapFromScene before the widget has even been added to the scene.
Add the Widget first then set its position. Also, if you think about it, you're going from global (screen) coordinates to local widget coordinates. This function may be of use, assuming the drop event coordinates are in screen space: -
QPoint QWidget::mapFromGlobal(const QPoint & pos) const
which Qt help defines as: -
Translates the global screen coordinate pos to widget coordinates.