總結:在mainwindows中添加一個QUndoStack和QUndoView。在構造函數中給edit[undoAction&redoAction]和view[Undo stack]添加不一樣的動做。windows
注意:edit中的undo和redo操做只須要添加響應的API就能夠了,qt編輯器會自動實現。可是view中的動做須要本身將這個動做關聯到一個槽函數實現:若是沒有undoview視圖就建立一個編輯器
將QUndoStack做爲參數傳遞給scene的構造函數,同時用一個指針指向QUndoStack。函數
一、mainwindowsui
1.一、mainwindow.h中this
#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; // 撤銷堆棧 QUndoView* m_undoView; // 撤銷堆棧視圖 public slots: //添加槽方法的定義 void showMessage( QString ); // 在狀態欄上顯示消息 void showUndoStack(); // 打開一個新的撤銷堆棧窗體 }; #endif // MAINWINDOW_H
1.二、mianwindow.cpp中spa
#include "mainwindow.h" #include <QMenuBar> #include <QStatusBar> #include <QGraphicsView> #include <QUndoStack> #include <QUndoView> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { //添加菜單到菜單欄裏面:添加下拉菜單 menuBar()->addMenu( "&File" ); QMenu* editMenu = menuBar()->addMenu( "&Edit" ); //將編輯得到視圖的菜單項指針存儲在本地變量裏面 QMenu* viewMenu = menuBar()->addMenu( "&View" ); //由於之後要用到 menuBar()->addMenu( "&Simulate" ); menuBar()->addMenu( "&Help" ); //新建undo堆棧和聯繫菜單操做 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()函數,用於說明快捷鍵 redoAction->setShortcut( QKeySequence::Redo ); //Ctrl+Y, Shift+Ctrl+Z:Qt的QKeySequence爲咱們定義了不少內置的快捷鍵 editMenu->addAction(undoAction); editMenu->addAction(redoAction); //在edit中添加兩個動做 statusBar()->showMessage("QSimulate has started"); // 建立場景和顯示場景的中央視圖部件 m_scene = new Scene(m_undoStack); //View是視圖,負責顯示;Scene是文檔,負責存儲數據。因此從這個角度出發,咱們能夠這樣認爲,一個Scene能夠關聯到多個View,就比如一份數據能夠有多個視圖去查看它同樣。 QGraphicsView* view = new QGraphicsView( m_scene ); //QGraphicsScene是一個視圖,它不可以單獨存在,必須關聯到至少一個QGraphicsView view->setAlignment( Qt::AlignLeft | Qt::AlignTop ); view->setFrameStyle( 0 ); setCentralWidget( view ); //將信號與槽關聯:m_scene發射message信號時,MainWindow接收信號,執行showMessage槽函數 connect( m_scene, SIGNAL(message(QString)), this, SLOT(showMessage(QString)) ); } MainWindow::~MainWindow() { } void MainWindow::showMessage( QString msg ) { statusBar()->showMessage( msg ); // 在主窗口狀態欄上顯示消息 } void MainWindow::showUndoStack() { if(m_undoView == 0) //判斷撤銷堆棧視圖是否被建立 { m_undoView = new QUndoView(m_undoStack); //建立而且設置窗體標題 m_undoView->setWindowTitle("QSimulate - Undo stack"); m_undoView->setAttribute( Qt::WA_QuitOnClose, false ); //撤銷堆棧視圖被用戶關閉應用程序也不會退出 } m_undoView->show(); }
二、Scene.net
2.一、Scene.h指針
#ifndef SCENE_H #define SCENE_H class QGraphicsSceneMouseEvent; class QUndoStack; //添加類QUndoStack的前置定義。 #include <QGraphicsScene> #include "sation.h" class Scene : public QGraphicsScene { Q_OBJECT public: Scene( QUndoStack* ); // 構造函數接受一個QUndoStack指針參數。 signals: void message( QString ); // 文本消息信號 protected: void mousePressEvent( QGraphicsSceneMouseEvent* ); // 接收鼠標按下事件 void contextMenuEvent( QGraphicsSceneContextMenuEvent* ); // 接收上下文菜單事件 private: QUndoStack* m_undoStack; // 撤銷堆棧存儲指針 }; #endif // SCENE_H
2.二、Scene.cppcode
#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 ) { // 設置本地變量和檢查被選中的電臺是否存在 qreal x = event->scenePos().x(); qreal y = event->scenePos().y(); QTransform transform; Sation *station = dynamic_cast<Sation *>(this->itemAt(QPointF(x, y), transform)); //pointf返回一個基類,如今讓他指向派生類 // 若是電臺沒有被選中而且鼠標左鍵被按下, 建立一個新電臺 if ( station == 0 && event->button() == Qt::LeftButton ) { addItem( new Sation( x, y ) ); emit message( QString("Station add at %1,%2").arg(x).arg(y) ); } // 調用基類的mousePressEvent處理其它鼠標按下事件 QGraphicsScene::mousePressEvent( event ); } /* * 添加新方法contextMenuEvent的代碼。當用戶在場景裏按下鼠標右鍵,這個方法將被調用。咱們只想用戶指向一個電臺時纔會顯示一個上下文菜單。 * 而後咱們建立一個只有刪除電臺選項的菜單。 * 若是用戶選擇這個選項,電臺將會從場景中刪除,而且從內存中刪除,和發送一條消息。 */ void Scene::contextMenuEvent( QGraphicsSceneContextMenuEvent* event ) { // 咱們只想當用戶選擇一個電臺時才顯示菜單 qreal x = event->scenePos().x(); qreal y = event->scenePos().y(); QTransform transform; Sation *station = dynamic_cast<Sation *>(this->itemAt(QPointF(x, y), transform)); //pointf返回一個基類,如今讓他指向派生類 if(0 == station) return; // 顯示上下文和相應的動做 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) ); } }
三、其他不變:這裏的undo關聯的是secne,和item沒有關係orm
3.一、sation.h
#ifndef SATION_H #define SATION_H #include <QGraphicsItem> /*新實現一個sation類,這個類能夠在scene中用,也能夠在mainwindow中用*/ class Sation : public QGraphicsItem { public: Sation(qreal, qreal); void paint(QPainter*, // // paint虛函數:繪製圖標 const QStyleOptionGraphicsItem*, QWidget* ); QRectF boundingRect() const{ // boundingRect虛函數:定義每一個圖標對應的外部邊框 return QRectF(-6.5, -13, 13, 18);//返回一個矩形:比咱們要畫的圖標稍大 } }; #endif // SATION_H
3.二、sation.cpp
#include "sation.h" #include <QPainter> Sation::Sation(qreal x, qreal y):QGraphicsItem() { setPos(x, y); setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIgnoresTransformations ); } //構造函數裏咱們根據x和y傳遞的參數設置座標。在paint函數繪製咱們的電臺圖標。 void Sation::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { //繪製電臺圖標,必須小於邊框矩形 painter->setRenderHint( QPainter::Antialiasing ); painter->setPen( QPen( Qt::black, 2 ) ); painter->drawRect( -4, -3, 8, 7 ); //畫一個矩形:以鼠標點擊的位置做爲起始中心 painter->drawLine( 0, -4, 0, -11 ); painter->drawLine( -5, -11, 0, -6 ); painter->drawLine( +5, -11, 0, -6 ); }
四、main.cpp
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }