qt之二維繪圖:添加一個撤銷堆棧菜單實現

總結:在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();
}

相關文章
相關標籤/搜索