Okay, so I've started making a game using Qt so that I can learn both Qt and C++ at the same time :D
However, I'm stuck with an issue at the moment.
I'm trying to make a textbox using a QGraphicsRectItem as a container (parent), and a QGraphicsTextItem as the text itself (child). The problem I'm facing is the child's relative position to the parent. If I set a font on the QGraphicsTextItem, the positioning will be completely wrong, and it will flow outside of the container for that matter.
TextBox.h:
#ifndef TEXTBOX_H
#define TEXTBOX_H
#include <QGraphicsTextItem>
#include <QGraphicsRectItem>
#include <QTextCursor>
#include <QObject>
#include <qDebug>
class TextBox: public QObject, public QGraphicsRectItem {
Q_OBJECT
public:
TextBox(QString text, QGraphicsItem* parent=NULL);
void mousePressEvent(QGraphicsSceneMouseEvent *event);
QString getText();
QGraphicsTextItem* playerText;
};
#endif // TEXTBOX_H
TextBox.cpp
#include "TextBox.h"
TextBox::TextBox(QString text, QGraphicsItem* parent): QGraphicsRectItem(parent) {
// Draw the textbox
setRect(0,0,400,100);
QBrush brush;
brush.setStyle(Qt::SolidPattern);
brush.setColor(QColor(157, 116, 86, 255));
setBrush(brush);
// Draw the text
playerText = new QGraphicsTextItem(text, this);
int xPos = rect().width() / 2 - playerText->boundingRect().width() / 2;
int yPos = rect().height() / 2 - playerText->boundingRect().height() / 2;
playerText->setPos(xPos,yPos);
}
void TextBox::mousePressEvent(QGraphicsSceneMouseEvent *event) {
this->playerText->setTextInteractionFlags(Qt::TextEditorInteraction);
}
Game.cpp (where the code for creating the object and such is located - only included the relevant part):
// Create the playername textbox
for(int i = 0; i < players; i++) {
TextBox* textBox = new TextBox("Player 1");
textBox->playerText->setFont(QFont("Times", 20));
textBox->playerText->setFlags(QGraphicsItem::ItemIgnoresTransformations);
scene->addItem(textBox);
}
Using the default font & size for the QGraphicsTextItem:
Setting a font & size for the QGraphicsTextItem:
The problem, as you may see, is that when I set a font and a size, the text is no longer in the center of the parent element. (Please do not unleash hell on me for bad code, I'm very new to both Qt and C++, and I'm doing this for learning purposes only).
You are calling the boundingRect() method in the constructor, so the position is set before the font is set to it's final value. If you either make a method to set the position and call it after setting the font or set the font before setting the position in the constructor it should work.
Related
I've been noticing an odd behavior with QSlider, which can be reproduced whenever slider widgets have different width, eg. being resized. Is it a problem with my signals? if so, how would I go about updating slider2 when slider1 has been updated, without affecting the width of the slider?
Visual:
The problem:
Changing the width of slider1, repaints slider2 to look the same, but repositioning slider2 manually will show that the length hasn't been changed, making it look like a glitch. Any ideas what's going on here or if it's a bug?
Expected behavior:
The sliders should not have any connection to each other regarding their layout, the only change I'd expect to see, is the knob being moved the corresponding position on the resized widget, not the exact same position.
Actual behavior:
The knob moves to the exact same position on the widget it signals to, instead of an offset position that should match its new width.
The following example was compiled using:
Qt 5.12.3 on Mac OS 10.14.5
Example
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QHBoxLayout>
#include <QSlider>
#include <QMainWindow>
#include <QDebug>
#include <QDockWidget>
class DockSliderWidget : public QDockWidget
{
Q_OBJECT
public:
DockSliderWidget(QWidget* parent = nullptr)
: QDockWidget(parent)
{
QWidget* container = new QWidget(this);
QHBoxLayout* hBoxLayout = new QHBoxLayout();
mSlider = new QSlider(Qt::Horizontal, this);
mSlider->setMinimum(0);
mSlider->setMaximum(100);
connect(mSlider, &QSlider::valueChanged, this, &DockSliderWidget::valueChanged);
hBoxLayout->addWidget(mSlider);
container->setLayout(hBoxLayout);
setWidget(container);
}
void changeValue(qreal value)
{
qDebug() << value;
QSignalBlocker b(mSlider);
mSlider->setValue(value);
}
Q_SIGNALS:
void valueChanged(qreal value);
private:
QSlider* mSlider = nullptr;
};
class DockSliderWidget;
class Widget : public QMainWindow
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr)
: QMainWindow(parent)
{
sliderWidget1 = new DockSliderWidget(parent);
sliderWidget2 = new DockSliderWidget(parent);
connect(sliderWidget1, &DockSliderWidget::valueChanged, sliderWidget2, &DockSliderWidget::changeValue);
connect(sliderWidget2, &DockSliderWidget::valueChanged, sliderWidget1, &DockSliderWidget::changeValue);
addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, sliderWidget1);
addDockWidget(Qt::DockWidgetArea::LeftDockWidgetArea, sliderWidget2);
}
~Widget()
{
}
private:
DockSliderWidget* sliderWidget1;
DockSliderWidget* sliderWidget2;
};
#endif // WIDGET_H
I'm trying to make a custom color picker, I should have everything I need to fill in the image with the colors. When I run the program it displays nothing only a the widget w/o colors that I can move to the left or top.
I added the QWidget in the designer tab, promoted it to the new class I made named, "ColorPicker". I added a protected function paintEvent in my header file.
My CPP file of ColorPicker:
#include "colorselector.h"
ColorSelector::ColorSelector(QWidget *parent) : QWidget(parent)
{
}
void ColorSelector::paintEvent(QPaintEvent *){
QColor c;
QImage pic(360,20,QImage::Format_RGB32);
for(int x=0; x < 360; x++){
for(int y=0; y < 20; y++){
c.setHsl(x,255,127);
pic.setPixel(x,y,c.rgb());
}
}
}
My header file of ColorPicker:
#ifndef COLORSELECTOR_H
#define COLORSELECTOR_H
#include <QWidget>
class ColorSelector : public QWidget
{
Q_OBJECT
public:
explicit ColorSelector(QWidget *parent = nullptr);
protected:
void paintEvent(QPaintEvent *);
signals:
public slots:
};
#endif // COLORSELECTOR_H
It should give me a rainbow of colors in a QWidget.
This is not what do you exactly want but may show you a good vision for doing that.
you can have three QSlider which represent 3 main colors red green and blue the value of sliders should be ranged between 0 to 255 for each of them.
Then create a square Qlabel and paint the backgroundColor of it by css with sliders value something like this :
QLabel* label = new QLabel;
label->->setStyleSheet("background-color:rgb(slider1->value();,sliderGreen->value();,slideBlue->value(););");
Dude it just a concept of the code and its not really code please reWrite it not paste it thanks :)
in the color selector you can paint the background of the widget using the r,g,b,a from a Qslider for example
void paintEvent(QPaintEvent* e) {
QPainter painter{this};
//and now fill the background!
painter.setBrush(QColor(r.value(), g.value(), b.value(), a.value()));
Have a look at this custom Circular Color Picker for PyQt
Circular PyQt ColorPicker
It looks like this and can easily added as a Widget and modified
I'm trying to create an application using Qt3D that where I can create multiple view windows on the same scene. I started with the code from the Qt3DWindow and the Simple C++ Example and started moving things around. What I figured is that each view window would define its own frame graph (just using a simple QForwardRenderer for now) and camera and then I would add each window's frame graph to the main frame graph in my scene.
Everything appears to be working fine as I create multiple windows, but when I close the windows and start removing frame graphs, the application crashes. It's crashing on a background thread somewhere down in the Qt3DCore or Qt3DRender module and I can't get to the source code. As I understand it I should be able to modify the frame graph dynamically at run time, but is that not thread safe? Are you expected to wholesale replace one frame graph with another as opposed to modifying the active frame graph like I'm doing?
--- Edit ---
I did a little more testing and if I delay destroying the QWindow (i.e., the surface that it's trying to render to) a bit after removing its frame graph from the parent frame graph, I don't get the crash. I do however get some warnings on the console that say:
Qt3D.Renderer.Backend: bool __cdecl Qt3DRender::Render::GraphicsContext::makeCurrent(class QSurface *) makeCurrent failed
My guess is it's a threading issue, that the backend is still trying to use the QSurface to render to after it has been destroyed on the main thread. I don't really like my solution (I just used a single shot timer to delay destroying the window by 1 second), but it's better than crashing.
RenderWindow.h
#ifndef RENDERWINDOW_H
#define RENDERWINDOW_H
#include <QWindow>
#include <Qt3DCore>
#include <Qt3DRender>
#include <Qt3DInput>
#include <Qt3DExtras/QForwardRenderer>
class RenderWindow : public QWindow
{
public:
RenderWindow(QScreen* screen = nullptr);
~RenderWindow();
Qt3DRender::QCamera* camera() const;
Qt3DRender::QFrameGraphNode* frameGraph() const;
protected:
void resizeEvent(QResizeEvent *) override;
private:
// Rendering
Qt3DRender::QFrameGraphNode* mpFrameGraph;
Qt3DRender::QCamera* mpCamera;
static bool msFormatDefined;
};
#endif // RENDERWINDOW_H
RenderWindow.cpp
#include "renderwindow.h"
#include <QDebug>
bool RenderWindow::msFormatDefined = false;
namespace
{
// Different clear colors so that it's obvious each window is using a
// different camera and frame graph.
static QColor sClearColors[] = {
Qt::darkBlue,
Qt::blue,
Qt::darkCyan,
Qt::cyan
};
static int sViewCount = 0;
}
RenderWindow::RenderWindow(QScreen* screen)
: QWindow(screen)
, mpFrameGraph(nullptr)
, mpCamera(new Qt3DRender::QCamera)
{
setSurfaceType(QWindow::OpenGLSurface);
// Set the default surface format once
if (!msFormatDefined)
{
QSurfaceFormat format;
format.setVersion(4, 3);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setDepthBufferSize(24);
format.setSamples(4);
format.setStencilBufferSize(8);
setFormat(format);
QSurfaceFormat::setDefaultFormat(format);
msFormatDefined = true;
}
// Camera
mpCamera->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f);
mpCamera->setPosition(QVector3D(0, 0, 40.0f));
mpCamera->setViewCenter(QVector3D(0, 0, 0));
// Frame Graph (using forward renderer for now)
Qt3DExtras::QForwardRenderer* renderer = new Qt3DExtras::QForwardRenderer;
renderer->setCamera(mpCamera);
renderer->setSurface(this);
renderer->setClearColor(sClearColors[sViewCount++ % 4]);
mpFrameGraph = renderer;
}
RenderWindow::~RenderWindow()
{
qDebug() << "start ~RenderWindow";
// Unparent objects. Probably not necessary but it makes me feel
// good inside.
mpFrameGraph->setParent(static_cast<Qt3DCore::QNode*>(nullptr));
mpCamera->setParent(static_cast<Qt3DCore::QNode*>(nullptr));
delete mpFrameGraph;
delete mpCamera;
qDebug() << "end ~RenderWindow";
}
Qt3DRender::QCamera* RenderWindow::camera() const
{
return mpCamera;
}
Qt3DRender::QFrameGraphNode* RenderWindow::frameGraph() const
{
return mpFrameGraph;
}
void RenderWindow::resizeEvent(QResizeEvent *)
{
mpCamera->setAspectRatio((float)width()/(float)height());
}
Scene.h
#ifndef SCENE_H
#define SCENE_H
#include <Qt3DCore/QEntity>
#include <Qt3DInput/QInputAspect>
#include <Qt3DRender/QFrameGraphNode>
#include <Qt3DRender/QRenderAspect>
#include <Qt3DRender/QRenderSettings>
class RenderWindow;
class Scene
{
public:
Scene();
~Scene();
Qt3DCore::QEntityPtr rootNode() const;
void addView(RenderWindow* window);
private:
void setupScene();
private:
Qt3DCore::QEntityPtr mpRoot;
// Frame Graph
Qt3DRender::QFrameGraphNode* mpFrameGraph;
Qt3DRender::QRenderSettings* mpRenderSettings;
// Aspects
Qt3DCore::QAspectEngine* mpEngine;
Qt3DRender::QRenderAspect* mpRenderAspect;
Qt3DInput::QInputAspect* mpInputAspect;
};
#endif // SCENE_H
Scene.cpp
#include "scene.h"
#include <QDebug>
#include <QPropertyAnimation>
#include <Qt3DCore/QTransform>
#include <Qt3DRender/QClearBuffers>
#include <Qt3DExtras/QPhongMaterial>
#include <Qt3DExtras/QCylinderMesh>
#include <Qt3DExtras/QSphereMesh>
#include <Qt3DExtras/QTorusMesh>
#include "orbittransformcontroller.h"
#include "RenderWindow.h"
Scene::Scene()
: mpRoot(nullptr)
, mpFrameGraph(new Qt3DRender::QFrameGraphNode)
, mpRenderSettings(new Qt3DRender::QRenderSettings)
, mpEngine(new Qt3DCore::QAspectEngine)
, mpRenderAspect(new Qt3DRender::QRenderAspect)
, mpInputAspect(new Qt3DInput::QInputAspect)
{
mpEngine->registerAspect(mpRenderAspect);
mpRenderSettings->setActiveFrameGraph(mpFrameGraph);
setupScene();
mpRoot->addComponent(mpRenderSettings);
mpEngine->setRootEntity(mpRoot);
}
Scene::~Scene()
{
qDebug() << "start ~Scene";
mpEngine->setRootEntity(Qt3DCore::QEntityPtr());
mpRoot.clear();
delete mpEngine;
// mpRenderSettings and mpFrameGraph are children of the
// root node and are automatically destroyed when it is.
qDebug() << "end ~Scene";
}
Qt3DCore::QEntityPtr Scene::rootNode() const
{
return mpRoot;
}
void Scene::addView(RenderWindow* window)
{
// Add the window's frame graph to the main frame graph
if (window->frameGraph())
{
window->frameGraph()->setParent(mpFrameGraph);
}
}
void Scene::setupScene()
{
mpRoot.reset(new Qt3DCore::QEntity);
Qt3DCore::QEntity* entity = new Qt3DCore::QEntity;
entity->setParent(mpRoot.data());
// Create the material
Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial(entity);
material->setAmbient(Qt::black);
material->setDiffuse(QColor(196, 196, 32));
material->setSpecular(Qt::white);
// Torrus
Qt3DCore::QEntity *torusEntity = new Qt3DCore::QEntity(entity);
Qt3DExtras::QTorusMesh *torusMesh = new Qt3DExtras::QTorusMesh;
torusMesh->setRadius(5);
torusMesh->setMinorRadius(1);
torusMesh->setRings(100);
torusMesh->setSlices(20);
Qt3DCore::QTransform *torusTransform = new Qt3DCore::QTransform;
torusTransform->setScale3D(QVector3D(1.5, 1, 0.5));
torusTransform->setRotation(QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), -45.0f));
torusEntity->addComponent(torusMesh);
torusEntity->addComponent(torusTransform);
torusEntity->addComponent(material);
// Sphere
Qt3DCore::QEntity *sphereEntity = new Qt3DCore::QEntity(entity);
Qt3DExtras::QSphereMesh *sphereMesh = new Qt3DExtras::QSphereMesh;
sphereMesh->setRadius(3);
Qt3DCore::QTransform *sphereTransform = new Qt3DCore::QTransform;
/*OrbitTransformController *controller = new OrbitTransformController(sphereTransform);
controller->setTarget(sphereTransform);
controller->setRadius(20.0f);
QPropertyAnimation *sphereRotateTransformAnimation = new QPropertyAnimation(sphereTransform);
sphereRotateTransformAnimation->setTargetObject(controller);
sphereRotateTransformAnimation->setPropertyName("angle");
sphereRotateTransformAnimation->setStartValue(QVariant::fromValue(0));
sphereRotateTransformAnimation->setEndValue(QVariant::fromValue(360));
sphereRotateTransformAnimation->setDuration(10000);
sphereRotateTransformAnimation->setLoopCount(-1);
sphereRotateTransformAnimation->start();*/
sphereEntity->addComponent(sphereMesh);
sphereEntity->addComponent(sphereTransform);
sphereEntity->addComponent(material);
}
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "scene.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void createWindow();
private:
Ui::MainWindow *ui;
Scene* scene;
};
#endif // MAINWINDOW_H
MainWindow.cpp
#include "mainwindow.h"
#include <QDebug>
#include "ui_mainwindow.h"
#include "renderwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
scene(new Scene())
{
ui->setupUi(this);
connect(ui->createButton, &QPushButton::clicked, this, &MainWindow::createWindow);
}
MainWindow::~MainWindow()
{
qDebug() << "~MainWindow";
delete scene;
delete ui;
}
void MainWindow::createWindow()
{
RenderWindow* window = new RenderWindow();
scene->addView(window);
window->resize(640, 480);
window->show();
QVector3D pos[] = {
QVector3D(0, 0, 40),
QVector3D(0, 25, -30),
QVector3D(-20, -20, -20),
QVector3D(40, 0, 0)
};
static int count = 0;
window->camera()->setPosition(pos[count++%4]);
window->camera()->setViewCenter(QVector3D(0, 0, 0));
// Delete the window when it is closed.
connect(window, &QWindow::visibilityChanged, this, [=](bool on)
{
if (!on)
window->deleteLater();
});
}
I've thoroughly tested your example and draw the same conclusions. When you destroy the window too quickly, the application crashes, probably because Qt3D still tries to issue some OpenGL commands to the underlying QSurface. I think this is a bug that should be reported.
A 'cleaner' work-around of this problem could be to track the generated 3d windows in the main window. You could maintain a list of pointers to all windows that where generated (and probably closed by the user at the some point). The windows are finally destroyed in the destructor of the main window.
I had exactly the same problem. I was creating a class derived from Qt3DWindow in a dialog box so the user could preview the effects of the choices made, and the program crashed when the dialog exited. In fact on Windows this crash causes the debugger and Qt Creator to crash too!
I tried working around this in a variety of ways and some helped because it turns out that it is a threading issue that was fixed on the 23rd October:
https://github.com/qt/qt3d/commit/3314694004b825263c9b9f2d69bd85da806ccbbc
The fix is now to apply the patch, and recompile Qt. 5.11.3 (or perhaps 5.12) will be out quite soon I expect but this bug is a killer if you are using Qt3D in dialogs.
I am trying to solve a graphics problem using the latest version of Qt. The image below shows what I managed to get so far and I will use it to explain the expected result.
I'm using a main vertical layout and within the biggest widget of the layout there is a horizontal layout with only one child: the square widget. The expected behavior would be of course to have the square widget centered horizontally and taking up the biggest space available. It is not required to use the same layout configuration, but the look of the interface should be the same.
The image above has been obtained by setting a QSizePolicy of minimumExpanding for both vertical and horizontal to the square widget and by forcing it to be square with the following code:
void SquareWidget::resizeEvent(QResizeEvent *event) {
//This is an override to the QWidget method
QSize s = size();
if (s.height()<s.width()) {
resize(s.height(), s.height());
} else {
resize(s.width(), s.width());
}
return;
}
While trying to solve this problem I went through the documentation some of the answers on this website and I couldn't find a clear answer about how to do two tasks.
First problem: how to make the widget square and keep its aspect ratio?
In this question
it is said that the method heightForWidth () doesn't work in newer versions of qt, and after a test it doesn't work for me either. The above override of resizeEvent, on the other hand, causes recursion because there are calls to resize() (and as far as I understand the layout should handle the resizing).
Second problem: how to center the square?
I tried using the layout alignment properties (center horizontally and vertically) but they cause the widget size to be immutable.
Maybe I am not understanding something about how Qt handles the widget placement. Any suggestion or clarification will be greatly appreciated.
You can do it with a QGridLayout.
Please see the attached code.
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class MyWidget final : public QWidget
{
Q_OBJECT
protected:
virtual void resizeEvent(QResizeEvent * event) override;
};
class MainWindow final : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow() = default;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include <QGridLayout>
#include <QLabel>
#include <QResizeEvent>
#include <QSpacerItem>
void MyWidget::resizeEvent(QResizeEvent * event)
{
event->accept();
const QSize current_size = size();
const int min = std::min(current_size.width(), current_size.height());
resize(min, min);
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
auto main_widget = new QWidget;
auto header = new QLabel("Hello World");
auto center_widget = new MyWidget;
auto footer = new QLabel("Good bye World");
auto spacer_left = new QSpacerItem(10, 10, QSizePolicy::Expanding);
auto spacer_right = new QSpacerItem(10, 10, QSizePolicy::Expanding);
auto grid_layout = new QGridLayout(main_widget);
auto center_palette = center_widget->palette();
center_palette.setColor(QPalette::Background, Qt::blue);
center_widget->setAutoFillBackground(true);
center_widget->setPalette(center_palette);
grid_layout->addWidget(header, 0, 1);
grid_layout->addItem(spacer_left, 1, 0);
grid_layout->addWidget(center_widget, 1, 1);
grid_layout->addItem(spacer_right, 1, 2);
grid_layout->addWidget(footer, 2, 1);
header->setAlignment(Qt::AlignCenter);
footer->setAlignment(Qt::AlignCenter);
setCentralWidget(main_widget);
}
Please see the result here
Rather than trying to get the widget to keep itself square and centred it might be simpler to reparent it and put the required logic in the parent widget type.
So, something like...
class keep_child_square_and_centred: public QWidget {
using super = QWidget;
public:
explicit keep_child_square_and_centred (QWidget *parent = nullptr)
: super(parent)
, m_widget(nullptr)
{}
virtual void set_widget (QWidget *widget)
{
if ((m_widget = widget))
m_widget->setParent(this);
}
virtual QSize sizeHint () const override
{
return(m_widget ? m_widget->sizeHint() : super::sizeHint());
}
protected:
virtual void resizeEvent (QResizeEvent *event) override
{
super::resizeEvent(event);
fixup();
}
private:
void fixup ()
{
if (m_widget) {
QRect r(QPoint(), QSize(height(), height()));
r.moveCenter(rect().center());
m_widget->setGeometry(r);
}
}
QWidget *m_widget;
};
Then use as...
keep_child_square_and_centred w;
SquareWidget sq;
w.set_widget(&sq);
You may still need to play around with a few settings if the parent is in a layout though.
I have a label in my GUI that displays an image as a QPixmap. I want to be able to draw a continuous line on my image by simply clicking anywhere on the image to select the start point and then make a second point somewhere else by clicking on a other part of the image. The two points should connect immediately after placing the second point and I want to be able to continue that same line by placing more points on the image.
While I know how to draw something on a QPixmap, the mouse event which I need to use to get the coordinates for the points is really confusing me as I’m still fairly new to Qt.
Any examples for a solution would be much appreciated.
I suggest you to use QGraphicsView for this purpose. Use my code snippet which works perfectly.
Subclass QGraphicsScene:
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include <QGraphicsScene>
#include <QPoint>
#include <QMouseEvent>
class GraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
explicit GraphicsScene(QObject *parent = 0);
signals:
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent);
public slots:
private:
QPolygon pol;
};
#endif // GRAPHICSSCENE_H
.cpp file:
#include "graphicsscene.h"
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
GraphicsScene::GraphicsScene(QObject *parent) :
QGraphicsScene(parent)
{
addPixmap(QPixmap("G:/2/qt.jpg"));//your pixmap here
}
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
//qDebug() << "in";
if (mouseEvent->button() == Qt::LeftButton)
{
QPoint pos = mouseEvent->scenePos().toPoint();
pol.append(pos);
if(pol.size() > 1)
{
QPainterPath myPath;
myPath.addPolygon(pol);
addPath(myPath,QPen(Qt::red,2));
}
}
}
Usage:
#include "graphicsscene.h"
//...
GraphicsScene *scene = new GraphicsScene(this);
ui->graphicsView->setScene(scene);
ui->graphicsView->show();
Result:
If you want save new pixmap (or just get pixmap) as image, use this code:
QPixmap pixmap(ui->graphicsView->scene()->sceneRect().size().toSize());
QString filename("example.jpg");
QPainter painter( &pixmap );
painter.setRenderHint(QPainter::Antialiasing);
ui->graphicsView->scene()->render( &painter, pixmap.rect(),pixmap.rect(), Qt::KeepAspectRatio );
painter.end();
pixmap.save(filename);
With render() you can also grab different areas of your scene.
But this code can be better: we create and paint same polygon. If we can remember last painted point, then we can paint line by line (begin of line is end of last line). In this case we don't need all points, we need just last point.
As I promised(Code improvement):just provide additional variable QPoint last; instead of QPolygon pol; and use next code:
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
//qDebug() << "in";
if (mouseEvent->button() == Qt::LeftButton)
{
QPoint pos = mouseEvent->scenePos().toPoint();
if(last.isNull())
{
last = pos;
}
else
{
addLine(QLine(last,pos),QPen(Qt::red,2));
last = pos;
}
}
}
As you can see, you store only last point and paint only last line. User can clicks thousands time and now you not need to store this unnecessary points and do this unnecessary repainting.