Interactive bezier spline editing within a mainwindow UI
-
Hey guys,
I want to add a widget into my mainwindow UI where the user can drag the control points of a cubic bezier curve to reshape it how they see fit. I envision that this would involve some kind of QPainterPath instance tied with some mouse event logic. And I know that QPainterPath has a cubicTo function which creates a cubic bezier curve where you input the end point and control points, so I'm wondering if I could link up the mouse event logic to those control points to move the bezier around. As you can see I have a basic idea, but I have hit a wall with the little details and am having trouble understanding how all of this connects and how to properly implement this. If anyone can offer a solution for how to accomplish this, I would appreciate it dearly.
I don't really have any code to share because I haven't been able to get the QPainterPath to work with my UI :(
Any guidance would be greatly appreciated.~Luke
-
Hi,
One way you can do that is using the Qt Graphics View framework.
Create a custom QGraphicsItem that you will use to move the curve's reference points and a QPainterPath based on them. -
Thank you for the reply! I am aware of the availability of QGraphicsItem and QPainterPath. I have read through the documentation but am having trouble figuring out how to get started on implementing this into an existing mainwindow UI. Is it possible you could please provide an example and explanation? Thanks again.
-
Also, I am using Qt 6 with C++, and usually do all my coding in the Qt Creator IDE.
-
Thank you for the reply! I am aware of the availability of QGraphicsItem and QPainterPath. I have read through the documentation but am having trouble figuring out how to get started on implementing this into an existing mainwindow UI. Is it possible you could please provide an example and explanation? Thanks again.
@lukester88 said in Interactive bezier spline editing within a mainwindow UI:
am having trouble figuring out how to get started on implementing this into an existing mainwindow UI.
-
In Designer from the toolbox at the left, section Display Widgets, drag a Graphics View onto the central widget in your MainWindow. This is a widget, just like all the other widgets there (e.g. Label, Line Edit, Frame etc.), so you can put it wherever you could put those and do all the usual layout stuff and so on. This gives you a
QGraphicsViewwidget namedgraphicsView. -
A
QGraphicsViewneeds aQGraphicsSceneas its "model", but you cannot create that from Designer. Somewhere in your code --- probably in the MainWindow constructor after theui->setupUi(this)--- create anew QGraphicsSceneand assign it to theQGraphcisViewviagraphicsView->setScene(). -
Now you have a graphics view with a graphics scene behind it. You can start creating your
QGraphicsItems and adding them onto the scene. They will be visualized in the view. You can do whatever to the scene or view to alter its look and behaviour.
-
-
Thank you! I ended spending some time today learning the basics of QGraphicsView and QPainter. I am able to get as far as plotting a cubic bezier using the cubicTo() function from QPainterPath, and then sending that path to the QGraphicsScene to be viewed in QGraphicsView. However, my next problem is getting mouse events to work. I have read the documentation on the QGraphicsView mouse event system, and it looks like Qt recommends subclassing a QWidget to create a custom graphics view class where I would override the virtual mouse event functions. I understand the basic idea, but can you explain how I would go about retrieving and using mouse event data from the QGraphicsView? Thank you
-
This post is deleted!
-
Thank you! I ended spending some time today learning the basics of QGraphicsView and QPainter. I am able to get as far as plotting a cubic bezier using the cubicTo() function from QPainterPath, and then sending that path to the QGraphicsScene to be viewed in QGraphicsView. However, my next problem is getting mouse events to work. I have read the documentation on the QGraphicsView mouse event system, and it looks like Qt recommends subclassing a QWidget to create a custom graphics view class where I would override the virtual mouse event functions. I understand the basic idea, but can you explain how I would go about retrieving and using mouse event data from the QGraphicsView? Thank you
-
To access the mouse you will need to use
mousePressEvent()andmouseMoveEvent()methods. These are available on both theQGraphicsViewandQGraphicsScene. A mouse event is sent first to the view and then to its scene. In both cases these arevirtual protectedmethods. That means you have to subclass the view/scene to intercept them. -
If you wanted to use them at the view level you would need to create your own
QGraphicsViewsubclass. Since you want to use Designer you would need to use its promotion feature to create a subclassedQGraphicsViewat design-time. -
However, I think you will be happy using them at the
QGraphicsScenelevel, where they have been translated into aQGraphicsSceneMouseEvent. So you will need to create your own subclass ofQGraphicsSceneto override the methods. Since you assign the scene to the view in your own code it's easy to just assign your subclassed instance. In those overridden methods you do whatever to the points of your curve. -
If you need to see mouse events on a
QGraphicsItem, like @SGaist mentioned, then similarly there are the same methods on those and you would subclass the items you add to the scene.
So to repeat: I think you will want to use mouse events on the graphics items or scene rather than on the view.
P.S.
Just came across how to use mouse move event for QGraphicsScene? from years ago. Might be worth looking at. -
-
Thank you! I have been working on implementing this method in my code, and I think I am 99% of the way there. I have created a subclass of QGraphicsScene. However, I am running into a segmentation fault when running the debugger, and when I try to run the application, it terminates abnormally and doesn't show the UI.
-
custom class header:
class customGraphicsScene : public QGraphicsScene { Q_OBJECT protected: void mousePressEvent(QGraphicsSceneMouseEvent *event) override; };class source file:
void customGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event) { if (event->button() == Qt::LeftButton) { qDebug() << "clicked!"; } }mainwindow implementation:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); customGraphicsScene *customScene; ui->graphicsView_2->setScene(customScene); } MainWindow::~MainWindow() { delete ui; } -
custom class header:
class customGraphicsScene : public QGraphicsScene { Q_OBJECT protected: void mousePressEvent(QGraphicsSceneMouseEvent *event) override; };class source file:
void customGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event) { if (event->button() == Qt::LeftButton) { qDebug() << "clicked!"; } }mainwindow implementation:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); customGraphicsScene *customScene; ui->graphicsView_2->setScene(customScene); } MainWindow::~MainWindow() { delete ui; }@lukester88 said in Interactive bezier spline editing within a mainwindow UI:
customGraphicsScene *customScene; ui->graphicsView_2->setScene(customScene);Think about it!
customSceneis just a pointer to somecustomGraphicsScene(and an uninitialized pointer at that). You have not allocated any actualcustomGraphicsSceneinstance for it to point it. I did say earlier:create a
new QGraphicsSceneand assign it to theQGraphcisViewviagraphicsView->setScene().you need to:
customGraphicsScene *customScene = new customGraphicsScene(this); ui->graphicsView_2->setScene(customScene);But a couple of points:
-
Your
customGraphicsScene *customSceneis a local variable in theMainWindowconstructor. Once that exits the local variable no longer exists, you have allocated acustomGraphicsScenebut you really can no longer get at it to do anything! You will want that pointer to be a member variable in yourMainWindowclass. -
class customGraphicsScene : public QGraphicsScene: make all your classes start with an uppercase letter. All Qt's do, and this is standard practice for C++. Your variables, such as [now]CustomGraphicsScene *customScene, should indeed start with a lowercase letter.
-