Using Qt Undo Framework With QPainter - c++

I am creating a drawing application with Qt and want to include undo and redo commands for whatever is drawn on a QImage. For this, I want to at least try and incorporate the Qt undo framework, since manually doing it will probably be above my skill level.
I have a MainWindow class where the main work is done and where I have the menu with undo and redo QActions.
The painting is done in a QWidget class called DrawingArea like this (PenShape is an enum of shapes):
void DrawingArea::drawLine(const QPoint &endPoint) {
QPainter painter(&image);
//some pen config code
switch (currentShape){
case PenShape::Polygon:
painter.drawPolygon(&endPoint, 5, Qt::OddEvenFill);
break;
case PenShape::Line:
painter.drawLine(lastPoint, endPoint);
break;
//other shapes and their draw methods..
}
update()
}
So as can be seen, the painter can draw in multiple shapes. I know I would have to somehow transfer this to a QUndoCommand class, but how? The drawline method is called only in DrawingArea's mouse event methods like this:
void DrawingArea::mousePressEvent(QMouseEvent *event){
if (event->button() == Qt::LeftButton) {
lastPoint = event->pos();
}
}
The QtUndoStack is located in MainWindow and the QActions are also there, but somehow I would have to connect the drawLine QtUndoCommand class and the mousePressEvent and mainWindow QAction with its connected slot method all together. This is what I don't know how to do.
Any help is appreciated!
Edit, here's the menu button actions:
m_undo = undoStack->createUndoAction(this, tr("&Undo"));
m_undo->setShortcuts(QKeySequence::Undo);
m_redo = undoStack->createRedoAction(this, tr("&Redo"));
m_redo->setShortcuts(QKeySequence::Redo);
I have no QUndoCommand classes because I'm unsure of how to create them yet.
In my MainWindow, I have a undoStack declared like this:
undoStack = new QUndoStack(this);

Related

Send Signal From Child To Parent On Mouse Click Qt

I have a MainWindow, a DockWidget and a QGLWidget inside it. I am drawing circles inside the QGLWidget. I want to print the x and y coordinates of the place that I am clicking to the labels inside DockWidget.
I would be glad if someone could show me the way?
Override the QWidget::mousePressEvent(QMouseEvent *) or QWidget::mouseReleaseEvent(QMouseEvent *) of the QGLWidget and create a signal for sending the x and y. It means you have to create own class inherithing from QGLWidget.
It could look like this:
void MyGLWidget::mouseReleaseEvent(QMouseEvent *event)
{
emit printXY(event->pos());
}
and in the MyGLWidget.h:
signals:
void printXY(const QPointF& clickedPos);
then create a slot in the DockWidget:
public slots:
void onPrintXY(const QPointF& clickedPos);
and connect it in the place where you create the MyGLWidget. I suppose in some function of the DockWidget like:
void DockWidget::createGLWidget()
{
MyGLWidget widget = new MyGLWidget;
connect(widget, &MyGLWidget::printXY, this, &DockWidget::onPrintXY);
}

Overlay multiple widgets

I have one QWidget which contains a multiple sliders. All sliders resized to main QWidget size. As result all sliders share same draw rectangle. For sliders I overload paintEvent method, so it draw only required stuff. Here is an example code:
class MySlider : public QSlider
{
void paintEvent(QPaintEvent *event) {
...
}
}
class MyWidget : public QWidget
{
MyWidget() : QWidget() {
slider1 = new MySlider(this);
slider2 = new MySlider(this);
slider1->resize(rect().width(), rect().height());
slider2->resize(rect().width(), rect().height());
}
MySlider * slider1;
MySlider * slider2;
}
adsf
Groove is not seen with this solution (because we don't call QSlider::paintEvent), but it still exist. For this widget it is possible to use only the last created slider (slider2). The rest are visible, but they are not available.
Is it possible to overlay multiple widgets on each other and still be able to access all of them with mouse event?
Overlapping widgets isn't a good idea, expect only one is visible at the same time. What is the purpose for that overlapping?
You can set QWidget::setAttribute(Qt::WA_TransparentForMouseEvents) to not generate any mouse events for that particular widget so that only one slider will get that events. Then you are able to redirect that messages to your other sliders.

Qt: Pass QGraphicsSceneContextMenuEvent from QGraphicsView

I have derived from both QGraphicsView and QGraphicsRectItem. I overloaded the contextMenuEvent on both classes to provide popup menus. I want the QGraphicsView context menu when you click on white space the the QGraphicsItem popup menu when you click on an item.
At first implementation, I got the QGraphicsView popup no matter where I clicked. So I modified the contextMenuEvent as follows:
void CustomGraphicsView::contextMenuEvent(QContextMenuEvent* event)
{
if (QGraphicsItem *item = itemAt(event->pos())) {
MyRect* rect = dynamic_cast<MyRect*>(item);
QGraphicsSceneContextMenuEvent* context_event = dynamic_cast<QGraphicsSceneContextMenuEvent*>(event);
if (rect && context_event)
rect->contextMenuEvent(context_event);
}
else {
QMenu menu;
... create the QGraphicsView popup menu
}
}
The dynamic_cast for the QGraphicsSceneContextMenuEvent fails so I never call the contextMenuEvent for the rect. It won't compile if I just try to pass the event to the rect->contextMenu(), so I tried the cast.
What is the right way to do this?
This is a learning project to just create/move/rotate/delete 2D shapes using Qt. If someone wants to look at the whole thing, let me know.
OK, so I figured it out. Just make sure to pass the event through the base class method. Simple! This also works for the mousePressEvent(), mouseMoveEvent(), and mouseReleaseEvent functions.
void CustomGraphicsView::contextMenuEvent(QContextMenuEvent* event)
{
// if the event is on a GGraphicsItem just pass the event along
if (itemAt(event->pos())) {
QGraphicsView::contextMenuEvent(event);
}
else
{
QMenu menu;
... create popup for the CustomGraphicsView

Paint over top of label, not behind it in Qt

I am creating a simple gauge in Qt 4.7.4, and everything is working wonderfully. Except for the fact that, for the life of me, I cannot get the dial shape to paint over the text labels when it passes over them. It always paints it behind the label. I am just using a simple drawpolygon() method.
I'm thinking this has something to do about paint events? I am drawing everything inside a QFrame inside a MainWindow. I am using QFrame's paintEvent.
Edit:
The QLabels are created on start up with new QLabel(this). They are only created once, and never touched again ( Similar to manually adding them on the Ui with Designer). The drawpolygon() is in the QFrame's Paint event.
"myclass.h"
class gauge : public QFrame
{
Q_OBJECT
public:
explicit gauge(QWidget *parent = 0);
~gauge();
void setValues(int req, int Limit, bool extra=false);
private:
void drawDial();
protected:
void paintEvent(QPaintEvent *e);
};
"myclass.cpp"
void gauge::paintEvent(QPaintEvent *e)
{
Q_UNUSED(e);
drawDial();
return;
}
void gauge::drawDial()
{
QPainter Needle(this);
Needle.save();
Needle.setRenderHint(Needle.Antialiasing, true); // Needle was Staggered looking, This will make it smooth
Needle.translate(centrePt); // Center of Widget
Needle.drawEllipse(QPoint(0,0),10,10);
Needle.restore();
Needle.end();
}
If the gauge widget and the QLabels are siblings, then you can move the gauge widget to the front by calling its raise() method.
If the QLabels are children of the gauge widget, on the other hand, then they will always display in front of it. In that case you can either reorganize your widget hierarchy so that they are siblings instead, or you can get rid of the QLabels and simply call drawText() from your paintEvent() method instead (after drawDial() returns)

Qt - Pass a QMouseEvent to child wildgets

I have do some research about QMouseEvents but I am stuck about passing a QMouseEvent to child widgets. I have a program with a structure like this :
MainWindow > DockWidget > WidgetList > WidgetTarget
MainWindow is parent of DockWidget etc... My primary goal is to know when I clicked in MainWindow and pass this QMouseEvent to WidgetTarget.
I read the doc about QMouseEvent and see the useful functions ignore() of QEvent but it does the contrary of what I want. The QMouseEvent is passed to the parent, so If I clik in WidgetTarget, the QMousseEvent will be pass to WidgetList.
So is there a way to pass a QMousseEvent to a child widget instead of its parent widget ?
I have see some trick with the flag Qt::WA_TransparentForMouseEvents but I don't know if this is a correct way
EDIT :
I will put some detail about the work in WidgetTarget with mousePressEvent(QMouseEvent *event). It basicaly to make a eyedropper. Here the code I have in mind :
void WidgetTarget::mousePressEvent(QMouseEvent *event)
{
if(eyeDropperActivated) //true when clicked on button eyedropper
{
QLabel *label = (QLabel*)MainWindow->childAt(event->x(),event->y());
QColor color; //Get the pixel value at x,y event from the QLabel pixmap
setColor(color) //Set the color parameter of WidgetTarget
}
}
I read your useful comments and yes I think It will be easy If I implement this code on MainWindow, but the eyedropper function is in WidgetTarget, so basically I have to find way to activate the eyedropper in WidgetTarget, check in the MainWindow if the eyedropper is activated, and send after to the WidgetTarget a signal with a QColor for example ?
Best Regards