I'm writing simple application by using QPixmap and QPainter. In my programm I need to load some images and resize them. I used QPixmap::scaled(), but images does not scaled. What I did wrong?
This is my code:
chesstile.cpp
#include "chesstile.h"
ChessTile::ChessTile(QWidget *parent) :
QLabel(parent)
{
}
void ChessTile::paintEvent(QPaintEvent *)
{
const QString &fileName = "images/white_king.png";
QPixmap bgPixmap(fileName);
bgPixmap.scaled(QSize(64, 64));
QPainter painter(this);
painter.drawPixmap(0, 0, bgPixmap);
}
chesstile.h
#ifndef CHESSTILE_H
#define CHESSTILE_H
#include <QLabel>
#include <QString>
#include <QPainter>
#include <QPixmap>
#include <QSize>
class ChessTile : public QLabel
{
Q_OBJECT
public:
ChessTile(QString fileName,
QString tileColor,
QWidget *parent = 0);
void paintEvent(QPaintEvent *);
private:
signals:
public slots:
};
#endif // CHESSTILE_H
You'll notice from the docs that the QPixmap::scaled member function is const - i.e. it doesn't change the object itself.
The scaled object is returned by that method, it doesn't change the original pixmap.
Try something like:
QPixmap bgPixmap(fileName);
QPixmap scaled = bgPixmap.scaled(QSize(64, 64));
QPainter painter(this);
painter.drawPixmap(0, 0, scaled)
Related
I've created a custom Widget: in the designer and promoted a QWidget to a TreeItem (my own). But when I run the code, I can't see the widget. The only way to see it is to use setFixedSize(100, 100). Even resize and setGeometrydon't work.
I want to be able to see my custom widget without giving it a fixed size.
TreeItem.h
#ifndef TREEITEM_H
#define TREEITEM_H
#include <QWidget>
#include <QRect>
class TreeItem : public QWidget
{
Q_OBJECT
public:
explicit TreeItem(QRect rect, QWidget *parent = nullptr);
virtual void paintEvent(QPaintEvent*);
signals:
public slots:
protected:
QRect m_rect;
QFont m_font;
};
#endif // TREEITEM_H
TreeItem.cpp
#include "treeitem.h"
TreeItem::TreeItem(QRect rect, QWidget *parent): QWidget(parent)
{
m_rect = rect;
m_font = QFont("Arrial", 20);
setFixedSize(100, 100); // doesn't show up without this line
}
void TreeItem::paintEvent(QPaintEvent*)
{
QPainter painter(this);
painter.drawLine(0, 0, 100, 100);
}
Problem
When QPainter is created after glClear the latter has no effect.
Description
I use Qt 5.7.1. I get same results with gcc on Linux and vc++ on Windows.
I have the following in a widget derived from QGLWidget:
void CanvasWidget::initializeGL()
{
qglClearColor(m_backgroundColor);
}
V1:
void CanvasWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT);
QPainter painter(this);
painter.drawLine(0, 0, 1000, 1000);
}
Produces:
V2:
void CanvasWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT);
}
Produces:
What I want is:
Which can be done with a hack:
void CanvasWidget::paintGL()
{
QPainter painter(this);
qglClearColor(m_backgroundColor);
glClear(GL_COLOR_BUFFER_BIT);
painter.drawLine(0, 0, 1000, 1000);
}
Question
What is going on? Why can't glClean and QPainter work together? Why can't I get it with V1?
Minimal Reproducible Example
main.cpp
#include "MainWindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow mainwindow;
mainwindow.show();
return app.exec();
}
MainWindow.h
#pragma once
#include "CanvasWidget.h"
#include <QMainWindow>
#include <memory>
class MainWindow : public QMainWindow
{
public:
explicit MainWindow(QWidget *parent = 0);
MainWindow(const MainWindow &) = delete;
MainWindow & operator= (const MainWindow &) = delete;
virtual ~MainWindow() = default;
private:
std::unique_ptr<CanvasWidget> m_canvasWidget;
};
MainWindow.cpp
#include "MainWindow.h"
#include <QHBoxLayout>
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
, m_canvasWidget(new CanvasWidget(parent))
{
setCentralWidget(m_canvasWidget.get());
}
CanvasWidget.h
#pragma once
#include <QGLWidget>
class CanvasWidget : public QGLWidget
{
Q_OBJECT
public:
CanvasWidget(QWidget* parent = 0, const QGLWidget* shareWidget = 0, Qt::WindowFlags f = 0);
private:
virtual void initializeGL() override;
virtual void paintGL() override;
private:
QColor m_backgroundColor;
};
CanvasWidget.cpp
#include "CanvasWidget.h"
#include <QMessageBox>
#include <QWheelEvent>
CanvasWidget::CanvasWidget(
QWidget* parent /*= 0*/,
const QGLWidget* shareWidget /*= 0*/,
Qt::WindowFlags f /*= 0 */)
: QGLWidget(parent, shareWidget, f)
, m_backgroundColor(0, 93, 196)
{}
void CanvasWidget::initializeGL()
{
qglClearColor(m_backgroundColor);
}
void CanvasWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT);
QPainter painter(this);
painter.drawLine(0, 0, 1000, 1000);
}
Adding setAutoFillBackground(false) in CanvasWidget constructor solves the problem.
All the credit goes to #G.M.
In Qt tutorial about overpainting OpenGL with QPainter it is stated:
When overpainting 2D content onto 3D content, we need to use a
QPainter and make OpenGL calls to achieve the desired effect. Since
QPainter itself uses OpenGL calls when used on a QGLWidget subclass,
we need to preserve the state of various OpenGL stacks when we perform
our own calls
So, looking at your code in V1 I suppose that QPainter sets its own clear color state, and if you don't set your glClearColor explicitly, you will get what QPainter object has set.
And I would also suggest you to use a tool like RenderDoc or gDebugger to trace GL commands of your app to see exactly what happens under the hood.
I am trying to implement an image editor with some customized image edition tools not present in the Qt image classes. When the user clicks on the image scene to perform some operation, I want the image to be instantly updated in the GUI application, showing in real time to the user the changes (drawing pixels, zooming...). The problem is that the actions to edit the image when clicking can only be done (at least to my knowledge) inside a separate class (in the example I show below, such class is called GraphicsScene), and thus I don't know how to transfer the edited image to the MainWindow class.
In short, I would like to "send" the edited image from the GraphicsScene class to the MainWindow class immediately after the user performs an action to edit the image, and then make the latter one execute the code to update the screen in real time after each user action is performed on the image.
For the sake of clarity, I next show the scheme of the code I have for now.
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
Ui::MainWindow *ui;
private slots:
private:
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QApplication>
#include <QMouseEvent>
#include <QGraphicsSceneMouseEvent>
#include "graphicsscene.h"
extern QImage Image_original, Image_modified;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_actionOpen_Image_triggered()
{
QDir dir;
QString filename=QFileDialog::getOpenFileName(this,
tr("Open Background"),
path,
tr("Images (*.png *.bmp *.jpg *.jpeg);;All files (*.*)"));
Image_original.load(filename);
GraphicsScene * img = new GraphicsScene( this );
img->addPixmap(QPixmap::fromImage(Image_original));
ui->preview->setScene(img);
}
In order to be able to track the coordinates when clicking, and following some suggestions around the net, I created a subclass of QGraphicsScene called GraphicsScene, whose header file is:
graphicsscene.h
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QPointF>
#include <QList>
class GraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
explicit GraphicsScene(QObject *parent = 0);
virtual void mousePressEvent(QGraphicsSceneMouseEvent * mouseEvent);
signals:
public slots:
private:
int x, y;
};
#endif // GRAPHICSSCENE_H
Finally, to perform the image editions, the source file associated is:
graphicsscene.cpp
#include "graphicsscene.h"
#include <iostream>
using namespace std;
extern QImage Image_original, Image_modified;
GraphicsScene::GraphicsScene(QObject *parent) :
QGraphicsScene(parent)
{
this->setBackgroundBrush(Qt::gray);
}
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent * mouseEvent)
{
QGraphicsScene::mousePressEvent(mouseEvent);
if (mouseEvent->button()==Qt::LeftButton)
{
x=mouseEvent->scenePos().x();
y=mouseEvent->scenePos().y();
}
Image_modified=some_custom_image_editing_code(Image_original, x, y);
}
Ideally, I would like to execute the following action in MainWindow after the mousePressEvent is performed:
img->addPixmap(QPixmap::fromImage(Image_modified));
ui->preview->setScene(img);
I would highly appreciate any idea.
Since you want to click the item containing the pixmap it is not necessary to overwrite the QGraphicsScene mousePressEvent method, since you could click outside the image, it is better to overwrite that method in QGraphicsPixmapItem.
Instead of using extern to access the images, in Qt is better signals and slot, but only the classes that inherit from QObject can have these attributes, unfortunately QGraphicsPixmapItem does not inherit from this, but we can use it as interface.
graphicspixmapitem.h
#ifndef GRAPHICSPIXMAPITEM_H
#define GRAPHICSPIXMAPITEM_H
#include <QGraphicsPixmapItem>
#include <QObject>
class GraphicsPixmapItem : public QObject, public QGraphicsPixmapItem
{
Q_OBJECT
public:
explicit GraphicsPixmapItem(QObject *parent=0);
protected:
void mousePressEvent(QGraphicsSceneMouseEvent * event);
signals:
void newPixmap(const QPixmap p);
};
#endif // GRAPHICSPIXMAPITEM_H
graphicspixmapitem.cpp
#include "graphicspixmapitem.h"
#include <QGraphicsSceneMouseEvent>
GraphicsPixmapItem::GraphicsPixmapItem(QObject * parent):QObject(parent)
{
}
void GraphicsPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
QPoint p = QPoint(event->pos().x(), event->pos().y());
QPixmap pix = pixmap();
if(!pix.isNull()){
QRect rect(QPoint(), pix.rect().size()/2);
rect.moveCenter(p);
QPixmap modified = pix.copy(rect);
modified = modified.scaled(pix.rect().size(), Qt::KeepAspectRatioByExpanding);
emit newPixmap(modified);
}
QGraphicsPixmapItem::mousePressEvent(event);
}
In the previous code we created the signal newPixmap, this connected it with a slot called onNewPixmap in MainWindow.
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "graphicsscene.h"
#include "graphicspixmapitem.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_actionOpen_triggered();
void onNewPixmap(const QPixmap pixmap);
private:
Ui::MainWindow *ui;
GraphicsPixmapItem *item;
GraphicsScene *scene;
QPixmap original_pixmap;
QPixmap new_pixmap;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGraphicsView>
#include <QFileDialog>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
scene = new GraphicsScene(this);
ui->preview->setScene(scene);
item = new GraphicsPixmapItem;
/*ui->preview->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->preview->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);*/
scene->addItem(item);
connect(item, &GraphicsPixmapItem::newPixmap, this, &MainWindow::onNewPixmap);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_actionOpen_triggered()
{
QString path = QDir::homePath();
QString filename=QFileDialog::getOpenFileName(this,
tr("Open Background"),
path,
tr("Images (*.png *.bmp *.jpg *.jpeg);;All files (*.*)"));
original_pixmap = QPixmap(filename);
item->setPixmap(original_pixmap);
}
void MainWindow::onNewPixmap(const QPixmap pixmap)
{
new_pixmap = pixmap;
QFile file("new_file.png");
file.open(QIODevice::WriteOnly);
new_pixmap.save(&file, "PNG");
}
In that slot as test I save the image in the folder where the executable is generated.
In your code I see that you use QImage, if you want to continue using it you can convert QPixmap to QImage with:
new_pixmap.toImage()
The complete example is found here
I'm in the process of transitioning from PyQt5 to Qt5 in C++ and I'm having a bit of a tough time. I created a simple UI that contains a Qwidget called logo. I'm trying to make this widget an SVG rendering widget with this code:
QSvgRenderer renderer(QString(":/LogoSVG.svg"));
QImage image(500, 200, QImage::Format_ARGB32);
image.fill(0x000000);
QPainter painter(&image);
renderer.render(&painter);
In Python, I'd create a simple widget class that renders the SVG then in the UI form loader class I'd do
self.logo = SVGRender(self)
I'm trying to do the same thing in C++ Qt, so here's what I have so far and it's returning the error error: cannot convert ‘logoW’ to ‘QWidget*’ in assignment
example.h
#ifndef EXAMPLE_H
#define EXAMPLE_H
#include "ui_example.h"
class example : public QWidget
{
Q_OBJECT
public:
example(QWidget *parent = 0);
private:
Ui::example ui;
};
class logoW: public QWidget
{
Q_OBJECT
public:
logoW(QWidget *parent = 0);
};
#endif
example.cpp
#include <QtGui>
#include <QScreen>
#include <QApplication>
#include <QDesktopWidget>
#include <QCoreApplication>
#include <QSvgRenderer>
#include <QPainter>
#include <QImage>
#include <iostream>
#include "example.h"
using namespace std;
example::example(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
ui.logo = logoW(this)
}
logoW :: logoW(QWidget * parent = 0){
QSvgRenderer renderer(QString(":resources/LogoSvg.svg"));
QImage image(500, 200, QImage::Format_ARGB32);
image.fill(0xaaA08080);
QPainter painter(&image);
renderer.render(&painter);
}
Can someone show me what I'm doing wrong?
The variable ui.logo requires a pointer of the object, in your case it changes:
ui.logo = logoW(this);
to:
ui.logo = new logoW(this);
I also understand that you want to display the image in the logo widget. To do this you must implement the paintEvent method:
*.h
class logoW: public QWidget
{
Q_OBJECT
public:
logoW(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent *event);
};
*.cpp
logoW::logoW(QWidget *parent):QWidget(parent)
{
}
void logoW::paintEvent(QPaintEvent *event){
Q_UNUSED(event)
QSvgRenderer renderer(QString(":resources/LogoSvg.svg"));
QPainter painter(this);
renderer.render(&painter);
}
I want to make a program that shows the info of a QGraphicsItem (width and height) by clicking the option "info" inside a context menu I created with the QGraphicsSceneContextMenuEvent. Right now I´m just trying to call a function with a qDebug in it called info.
Here's my code
dialog.h:
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include "QtCore"
#include "QtGui"
#include "mysquare.h"
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
QGraphicsScene *scene;
MySquare *square;
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
#include <QWidget>
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
scene = new QGraphicsScene(this);
ui->graphicsView->setScene(scene);
square=new MySquare();
scene->addItem(square);
}
Dialog::~Dialog()
{
delete ui;
}
mysquare.h
#ifndef MYSQUARE_H
#define MYSQUARE_H
#include <QPainter>
#include <QGraphicsItem>
#include <QDebug>
#include <QMenu>
class MySquare: public QGraphicsItem
{
public:
MySquare();
QRectF boundingRect() const;
void paint (QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
QMenu *menu;
QAction *heightAct;
QAction *widthAct;
QAction *color;
private slots:
void info();
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
};
#endif // MYSQUARE_H
mysquare.cpp
#include "mysquare.h"
#include<QMenu>
#include <string>
using namespace std;
MySquare::MySquare()
{
}
QRectF MySquare::boundingRect() const
{
return QRectF(100,100,50,50);
}
void MySquare::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QRectF rec = boundingRect();
QBrush brushy(Qt::green);
painter->fillRect(rec,brushy);
painter->drawRect(rec);
}
void MySquare::info()
{
qDebug()<<"HERE'S MY INFO!!!";
}
void MySquare::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
QString height = "Height: "+QString::number(boundingRect().height());
QString width = "Width: "+QString::number(boundingRect().width());
menu = new QMenu();
heightAct = menu->addAction(height);
widthAct = menu->addAction(width);
color = menu->addAction("Change color");
menu->exec(QCursor::pos());
}
To make a "link" between a click on an action in your menu and your slot info(), you need to use the signal/slot mechanism.
You only have to add a QObject::connect(...) in your QMenu initialization.
void MySquare::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
QString height = "Height: "+QString::number(boundingRect().height());
QString width = "Width: "+QString::number(boundingRect().width());
menu = new QMenu();
heightAct = menu->addAction(height);
widthAct = menu->addAction(width);
color = menu->addAction("Change color");
QObject::connect(heightAct, SIGNAL(triggered()), this, SLOT(info()));
menu->exec(QCursor::pos());
}
However, the above code will not compile because signal/slot mechanism work only with subclasses of QObject and QGraphicsItem isn't. So you have to change it to a QGraphicsObject. (Don't forget the Q_OBJECT macro though)
Also, a little out of subject:
Use
#include <QtCore>
instead of
#include "QtCore"
but I really advise you to just include what you need, not the whole QtCore library.
Also, put your includes in your source files instead of your header files except when it's impossible.
Don't mix standard C++ library such as
#include <string>
except when it's absolutely needed. In this case you have to include
#include <QString>
And just simply remove
using namespace std;