文档章节

qt之二维绘图:添加一个撤销堆栈菜单实现

O
 OceanStar
发布于 2018/09/04 13:00
字数 1474
阅读 69
收藏 1
Qt

总结:在mainwindows中添加一个QUndoStack和QUndoView。在构造函数中给edit[undoAction&redoAction]和view[Undo stack]添加不同的动作。

注意:edit中的undo和redo操作只需要添加响应的API就可以了,qt编辑器会自动实现。但是view中的动作需要自己将这个动作关联到一个槽函数实现:如果没有undoview视图就创建一个

将QUndoStack作为参数传递给scene的构造函数,同时用一个指针指向QUndoStack。

1、mainwindows

1.1、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;           // 撤销堆栈
    QUndoView*   m_undoView;            // 撤销堆栈视图

public slots:   //添加槽方法的定义
  void showMessage( QString );        // 在状态栏上显示消息
  void showUndoStack();               // 打开一个新的撤销堆栈窗体
};

#endif // MAINWINDOW_H

1.2、mianwindow.cpp中

#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();
}

2、Scene

2.1、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.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 )
{
    // 设置本地变量和检查被选中的电台是否存在
    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) );
    }
}

3、其余不变:这里的undo关联的是secne,和item没有关系

3.1、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.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
             );
}
//构造函数里我们根据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 );
}

 

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();
}

© 著作权归作者所有

O
粉丝 3
博文 163
码字总数 43958
作品 0
杭州
私信 提问
陈鲁勇/OEasyScreenshot

首页致词 这个项目,有幸得到你的浏览。 这是一个基于 Qt 5 开发的项目,Qt Creator 4 作为主要的开发 IDE, 项目打开方式,请移步 demo 文件夹,根据自己的编译器选择对应的 pro 工程文件。...

陈鲁勇
2017/04/28
0
0
Qt Quick实现的涂鸦程序

之前一直以为 Qt Quick 里 Canvas 才可以自绘,后来发觉不是,原来还有好几种方式都可以绘图!可以使用原始的 OpenGL(Qt Quick 使用 OpenGL 渲染),可以构造QSGNode 来绘图,还可以使用 QP...

foruok
2014/11/21
0
0
QCAD v3.23.0.0源码编译,使用VS2017+Qt5.9.8环境

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 https://blog.csdn.net/libaineu2004/article/details/100217789 QCAD 是一个免费、开源的计...

libaineu2004
09/02
0
0
Python GUI教程(十三):在GUI中绘制K线图

文章首发个人博客:http://zmister.com Python GUI教程(一):在PyQt5中创建第一个GUI图形用户界面 Python GUI教程(二):添加窗口小部件到图形用户界面GUI中 Python GUI教程(三):在GUI...

州的先生
2017/12/01
0
0
【QT技术应用】2D绘图

2D绘图 Qt4中的2D绘图部分称为Arthur绘图系统.它由3个类支撑整个框架,QPainter,QPainterDevice和QPainterEngine.QPainter用来执行具体的绘图相关操作 如画点,画线,填充,变换,alpha通道等...

长平狐
2012/09/03
1K
0

没有更多内容

加载失败,请刷新页面

加载更多

开发中常用的正则表达式

为了能够更好地理解如何在C#环境中使用正则表达式,这里整理了一些常用的正则表达式: 罗马数字: string p1 = "^m*(d?c{0,3}|c[dm])" + "(l?x{0,3}|x[lc])(v?i{0,3}|i[vx])$"; string t1 =......

木庄
22分钟前
4
0
【.NET程序打包】VS2019使用Installer Projects打包

C#—使用Installer Projects打包桌面应用程序 前言 打包桌面应用程序实在是一个不常使用的东西,偶尔使用起来经常会忘东忘西的耽误时间,因此,这篇文章多以图片记录过程,也是用于备忘。 下...

_Somuns
27分钟前
4
0
自定义注解,使用动态代理解决网站的字符集编码问题

第1章设置环境 安装操作系统,安装备份(镜像): JDK: 设置环境变量Eclipse:解压即可 Eclipse自身解压目录不包括中文 代码工作空间目录不包括中文Tomcat:解压不要包含中文目录M...

蓝来杯往
31分钟前
4
0
Solr中的字段类型field type

Solr含有多种字段类型,可用的字段类型基本都定义在了包org.apache.solr.schema中,列举如下: 类 说明 BinaryField 二进制数据 BoolField 布尔值,其中’t’/’T’/’1’都是true Collatio...

gantaos
45分钟前
4
0
《JAVA核心知识》学习笔记 (21. JAVA 算法)

21. JAVA 算法

Shingfi
52分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部