Summary: add a QUndoStack and QUndoView to mainwindows. Add different actions to edit [undo action & redoaction] and view[Undo stack] in the constructor.
Note: the undo and redo operations in edit only need to add the response API, and the qt editor will implement them automatically. But the action in view needs to be associated with a slot function implementation: if there is no undo view, create a
Pass QUndoStack as an argument to scene's constructor, and point to QUndoStack with a pointer.
1,mainwindows
1.1. In mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H /*https://blog.csdn.net/elf001/article/details/8980083*/ class Scene; class QUndoStack; class QUndoView; #include <QMainWindow> #include "scene.h" class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0); ~MainWindow(); private: Scene* m_scene; QUndoStack* m_undoStack; // Revocation stack QUndoView* m_undoView; // Undo stack view public slots: //Definition of adding slot method void showMessage( QString ); // Show messages on the status bar void showUndoStack(); // Open a new undo stack form }; #endif // MAINWINDOW_H
1.2. In mianwindow.cpp
#include "mainwindow.h" #include <QMenuBar> #include <QStatusBar> #include <QGraphicsView> #include <QUndoStack> #include <QUndoView> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { //Add menu to menu bar: add drop-down menu menuBar()->addMenu( "&File" ); QMenu* editMenu = menuBar()->addMenu( "&Edit" ); //Store the menu item pointer of the edited view in the local variable QMenu* viewMenu = menuBar()->addMenu( "&View" ); //Because it will be used later menuBar()->addMenu( "&Simulate" ); menuBar()->addMenu( "&Help" ); //New undo stack and contact menu operation m_undoStack = new QUndoStack(this); m_undoView = 0; viewMenu->addAction("Undo stack", this, SLOT(showUndoStack())); //https://blog.csdn.net/ljhandlwt/article/details/52354139 QAction *undoAction = m_undoStack->createUndoAction(this); QAction *redoAction = m_undoStack->createRedoAction(this); undoAction->setShortcut( QKeySequence::Undo ); //Ctrl+Z, Alt+Backspace:setShortcuts() function for shortcut keys redoAction->setShortcut( QKeySequence::Redo ); //QKeySequence of Ctrl+Y, Shift+Ctrl+Z:Qt defines many built-in shortcut keys for us editMenu->addAction(undoAction); editMenu->addAction(redoAction); //Add two actions to edit statusBar()->showMessage("QSimulate has started"); // Create a central view part of the scene and display it m_scene = new Scene(m_undoStack); //View is the view, responsible for displaying; Scene is the document, responsible for storing data. So from this point of view, we can think that a Scene can be associated with multiple views, just like a data can have multiple views to view it. QGraphicsView* view = new QGraphicsView( m_scene ); //Qgraphicscene is a view. It cannot exist alone. It must be associated with at least one QGraphicsView view->setAlignment( Qt::AlignLeft | Qt::AlignTop ); view->setFrameStyle( 0 ); setCentralWidget( view ); //Associate signal with slot: when m_scene transmits message signal, MainWindow receives signal and executes showMessage slot function connect( m_scene, SIGNAL(message(QString)), this, SLOT(showMessage(QString)) ); } MainWindow::~MainWindow() { } void MainWindow::showMessage( QString msg ) { statusBar()->showMessage( msg ); // Display messages on the main window status bar } void MainWindow::showUndoStack() { if(m_undoView == 0) //Determine whether the undo stack view is created { m_undoView = new QUndoView(m_undoStack); //Create and set the form title m_undoView->setWindowTitle("QSimulate - Undo stack"); m_undoView->setAttribute( Qt::WA_QuitOnClose, false ); //Undo stack view is closed by the user and the application will not exit } m_undoView->show(); }
2,Scene
2.1,Scene.h
#ifndef SCENE_H #define SCENE_H class QGraphicsSceneMouseEvent; class QUndoStack; //Add a pre definition of the QUndoStack class. #include <QGraphicsScene> #include "sation.h" class Scene : public QGraphicsScene { Q_OBJECT public: Scene( QUndoStack* ); // Constructor takes a QUndoStack pointer parameter. signals: void message( QString ); // Text message signal protected: void mousePressEvent( QGraphicsSceneMouseEvent* ); // Receive mouse down events void contextMenuEvent( QGraphicsSceneContextMenuEvent* ); // Receive context menu events private: QUndoStack* m_undoStack; // Undo stack store pointer }; #endif // SCENE_H
2.2,Scene.cpp
#include "scene.h" #include <QGraphicsSceneMouseEvent> #include <QGraphicsSceneContextMenuEvent> #include <QMenu> #include <QAction> #include <QUndoStack> Scene::Scene( QUndoStack* undoStack ) : QGraphicsScene() { addLine( 0, 0, 0, 1, QPen(Qt::transparent, 1) ); m_undoStack = undoStack; } void Scene::mousePressEvent( QGraphicsSceneMouseEvent* event ) { // Set local variables and check whether the selected station exists qreal x = event->scenePos().x(); qreal y = event->scenePos().y(); QTransform transform; Sation *station = dynamic_cast<Sation *>(this->itemAt(QPointF(x, y), transform)); //pointf returns a base class, now let it point to a derived class // If the station is not selected and the left mouse button is pressed, create a new station if ( station == 0 && event->button() == Qt::LeftButton ) { addItem( new Sation( x, y ) ); emit message( QString("Station add at %1,%2").arg(x).arg(y) ); } // Call mousePressEvent of the base class to handle other mouse down events QGraphicsScene::mousePressEvent( event ); } /* * Add the code for the new method contextMenuEvent. When the user presses the right mouse button in the scene, this method will be called. We only want a context menu to be displayed when the user points to a station. * Then we create a menu with only the delete radio option. * If the user selects this option, the station will be removed from the scene, removed from memory, and a message will be sent. */ void Scene::contextMenuEvent( QGraphicsSceneContextMenuEvent* event ) { // We only want to show the menu when the user selects a station qreal x = event->scenePos().x(); qreal y = event->scenePos().y(); QTransform transform; Sation *station = dynamic_cast<Sation *>(this->itemAt(QPointF(x, y), transform)); //pointf returns a base class, now let it point to a derived class if(0 == station) return; // Display context and corresponding actions QMenu menu; QAction* deleteAction = menu.addAction("Delete Station"); if ( menu.exec( event->screenPos() ) == deleteAction ) { removeItem( station ); delete station; emit message( QString("Station deleted at %1,%2").arg(x).arg(y) ); } }
3. The rest remains the same: the undo here is associated with secne, which has nothing to do with item
3.1,sation.h
#ifndef SATION_H #define SATION_H #include <QGraphicsItem> /*This class can be used in scene or mainwindow*/ class Sation : public QGraphicsItem { public: Sation(qreal, qreal); void paint(QPainter*, // //paint virtual functions: drawing icons const QStyleOptionGraphicsItem*, QWidget* ); QRectF boundingRect() const{ // boundingRect virtual function: define the external border corresponding to each icon return QRectF(-6.5, -13, 13, 18);//Returns a rectangle: slightly larger than the icon we want to draw } }; #endif // SATION_H
3.2,sation.cpp
#include "sation.h" #include <QPainter> Sation::Sation(qreal x, qreal y):QGraphicsItem() { setPos(x, y); setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIgnoresTransformations ); } //In the constructor, we set the coordinates according to the parameters passed by x and y. Draw our radio station icon in the paint function. void Sation::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { //Draw the station icon, which must be smaller than the rectangle of the border painter->setRenderHint( QPainter::Antialiasing ); painter->setPen( QPen( Qt::black, 2 ) ); painter->drawRect( -4, -3, 8, 7 ); //Draw a rectangle: use the position of the mouse click as the starting center painter->drawLine( 0, -4, 0, -11 ); painter->drawLine( -5, -11, 0, -6 ); painter->drawLine( +5, -11, 0, -6 ); }
4,main.cpp
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }