Qt Charts使用(重写QChartView,实现一些自定义功能)


请添加图片描述

1、charts简单使用

    QLineSeries* m_pSeries = new QLineSeries();
    QFile file("C:\\Users\\Administrator\\Desktop\\2.txt");
    file.open(QIODevice::ReadOnly | QIODevice::Text);
    QTextStream in(&file);
    while (!in.atEnd())
    {
        QString line = in.readLine();
        QStringList listLine = line.split("\t");
        // 从文件读取数据
		m_pSeries->append(listLine[0].toDouble(), listLine[1].toDouble());
    }
    file.close();

    // QChartView *m_pChartView = new QChartView(this);
    // 用重写QChartView做显示
    mycharts* m_pChartView = new mycharts(this);
    QChart* m_pChart = new QChart();
    
    m_pChart->addSeries(m_pSeries);
    m_pSeries->setUseOpenGL(true);
	m_pChart->createDefaultAxes();
    m_pChart->axes(Qt::Vertical).at(0)->setRange(-240, 240);
	m_pChart->axisX()->setRange(-160,160);
    m_pChart->legend()->hide();

    m_pChartView->setChart(m_pChart);
    m_pChartView->resize(QSize(600, 500));
    m_pChartView->setRenderHints(QPainter::Antialiasing);

2、重写QChartView

注意:提升性能方案
m_pSeries->setUseOpenGL(true);
不使用openGL时,3000个点左右就卡死,在上绘制会更卡,性能提升有限,提升了10倍左右

mycharts.h

#ifndef MYCHARTS_H
#define MYCHARTS_H

#include <QWidget>
#include <QtCharts/QLineSeries>
#include <QtCharts/QValueAxis>
#include <QChart>
#include <QChartView>
QT_CHARTS_USE_NAMESPACE
#include "mainwindow.h"

class mycharts : public QChartView
{
    Q_OBJECT
public:
    explicit mycharts(QWidget *parent = nullptr);

public:
    //鼠标在区域的哪个位置
    enum AreaPosition : int
    {
        Outside = 0x00,
        Inside = 0xFF, //任意值
        AtLeft = 0x01,
        AtRight = 0x02,
        AtTop = 0x10,
        AtBottom = 0x20,
        AtTopLeft = 0x11, //AtLeft|AtTop
        AtTopRight = 0x12, //AtRight|AtTop
        AtBottomLeft = 0x21, //AtLeft|AtBottom
        AtBottomRight = 0x22 //AtRight|AtBottom
    };
    //当前编辑类型
    enum EditType : int
    {
        EditNone, //无操作
        PressInside, //在选区范围内按下
        PressOutside, //在选区范围外按下
        DrawSelection, //绘制
        MoveSelection, //拖动
        EditSelection //拉伸编辑
    };
protected:
    void paintEvent(QPaintEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;

private:
    //计算鼠标相对区域的位置
    AreaPosition calcPosition(const QPoint &pos);
	AreaPosition calcPosition1(const QPoint &pos);

    //当前鼠标对应选区的位置
    void setCurPosition(AreaPosition position);
	void setCurPosition1(AreaPosition position);
    //根据鼠标当前位置更新鼠标样式
    void updateCursor();
	void updateCursor1();
private:
    //当前选区
    //QRect有四个成员变量,分别对应左上角和右下角点坐标
    //x1-左上角坐标x
    //x2-等于x1+width-1
    //y1-左上角坐标y
    //y2-等于y1+height-1
    //即QRect(50,50,200,200)时,topLeft=(50,50)bottomRight=(249,249)
    //fillRect会填充整个区域
    //drawRect在画笔宽度奇数时,右下角会多1px,绘制时整体宽度先减去1px
    QRect selection;
	QRect select1;
    //是否有选区
    bool hasSelection{false};
    //鼠标当前操作位置
    AreaPosition curPosition{AreaPosition::Outside};
	AreaPosition curPosition1{ AreaPosition::Outside };
    //当前操作类型
    EditType curEditType{EditType::EditNone};
	EditType curEditType1{ EditType::EditNone };
    //鼠标按下标志
    bool pressFlag{false};
	bool pressFlag1{ false };

    //鼠标按下位置
    QPoint pressPos;
    //目前用于记录press时鼠标与选区左上角的坐标差值
    QPoint tempPos;
	QPoint tempPos1;
    //鼠标当前位置
    QPoint mousePos;

    //最小宽度
    static const int Min_Width{5};

	int	m_xl;
	int m_yl;
	int m_xr;
	int m_yr;
	MainWindow*	m_pwindows;
signals:

public slots:
};

#endif // MYCHARTS_H

QChartView.cpp

#include "mycharts.h"
#include <qdebug.h>

mycharts::mycharts(QWidget *parent) : QChartView(parent)
{
	m_pwindows = (MainWindow*)parent;
    setMouseTracking(true);
    selection = QRect(85, 37, 60, 415);
    hasSelection = true;
	select1 = QRect(400, 37,60,415);
}

void mycharts::paintEvent(QPaintEvent *event)
{
    QChartView::paintEvent(event);
    QPainter painter(viewport());
    if (!hasSelection)
        return;

    painter.save();

    if (pressFlag && curPosition != AreaPosition::Outside)
    { //点击样式,选用纯正的原谅绿主题
        //painter.setPen(QColor(0, 255, 255));
		painter.setPen(QColor(205,104,57,100));
        painter.setBrush(QColor(46, 139, 87, 100));
    }
    else if (curPosition != AreaPosition::Outside)
    { //悬停样式
        painter.setPen(QColor(0, 255, 255,100));
        painter.setBrush(QColor(46, 139, 87, 100));
    }
    else
    { //未选中样式
        painter.setPen(QColor(0, 150, 255,100));
        painter.setBrush(QColor(46,139,87,100));
    }
    //-1是为了边界在rect范围内
    painter.drawRect(selection.adjusted(0, 0, -1, -1));


	if (pressFlag1 && curPosition1 != AreaPosition::Outside)
	{ //点击样式,选用纯正的原谅绿主题
	  //painter.setPen(QColor(0, 255, 255));
		painter.setPen(QColor(205, 104, 57,150));
		painter.setBrush(QColor(205, 104, 57,100));
	}
	else if (curPosition1 != AreaPosition::Outside)
	{ //悬停样式
		painter.setPen(QColor(139,115,85,100));
		//painter.setBrush(QColor(0, 160, 0));
		painter.setBrush(QColor(205, 104, 57, 100));
	}
	else
	{ //未选中样式
		painter.setPen(QColor(255,231,186,100));
		//painter.setBrush(QColor(0, 140, 0));
		painter.setBrush(QColor(205, 104, 57, 100));
	}
	painter.drawRect(select1.adjusted(0, 0, -1, -1));
    painter.restore();

}

void mycharts::mousePressEvent(QMouseEvent *event)
{
    QChartView::mousePressEvent(event);
    //event->accept();
     mousePos = event->pos();
     if (event->button() == Qt::LeftButton)
     {
		 pressFlag1 = true;
         //鼠标左键进行编辑操作
         pressFlag = true;

         pressPos = event->pos();
         if (curPosition == AreaPosition::Inside)
         {
             curEditType = PressInside;
             //鼠标相对选区左上角的位置
             tempPos = mousePos - selection.topLeft();
			 //tempPos1 = mousePos - select1.topLeft();
         }
         else if (curPosition != AreaPosition::Outside)
         {
             curEditType = EditSelection;
         }
         else
         {
             curEditType = PressOutside;
         }


		 if (curPosition1 == AreaPosition::Inside)
		 {
			 curEditType1 = PressInside;
			 tempPos1 = mousePos - select1.topLeft();
		 }
		 else if (curPosition1 != AreaPosition::Outside)
		 {
			 curEditType1 = EditSelection;
		 }
		 else
		 {
			 curEditType1 = PressOutside;
		 }
     }
     else
     {
         //非单独按左键时的操作
     }
     update();

}

void mycharts::mouseMoveEvent(QMouseEvent *event)
{
	//qDebug() << this->chart()->mapToValue(event->pos());
	//qDebug() << this->chart()->mapToValue(QPointF(QPoint(selection.left(),0))).x();
	//qDebug() << this->chart()->mapToValue(QPointF(QPoint(selection.right(), 0)));
	m_xl = this->chart()->mapToValue(QPointF(QPoint(selection.left(), 0))).x();
	m_yl = this->chart()->mapToValue(QPointF(QPoint(selection.right(), 0))).x();

	m_xr = this->chart()->mapToValue(QPointF(QPoint(select1.left(), 0))).x();
	m_yr = this->chart()->mapToValue(QPointF(QPoint(select1.right(), 0))).x();
	m_pwindows->setLine(m_xl, m_yl,m_xr,m_yr);

     QChartView::mouseMoveEvent(event);
       mousePos = event->pos();

	   if (curEditType == MoveSelection && curEditType1 == MoveSelection)
		   curEditType1 = PressOutside;

       if (pressFlag)
       {
           if (curEditType == PressInside)
           {
               //在选区内点击且移动,则移动选区
               if (QPoint(pressPos - mousePos).manhattanLength() > 3)
               {
                   curEditType = MoveSelection;
               }
           }
           QPoint mouse_p = mousePos;
           //限制范围在可视区域
           if (mouse_p.x() < 0)
           {
               //mouse_p.setX(0);
			   mouse_p.setX(0);
           }
           else if (mouse_p.x() > width() - 1)
           {
               mouse_p.setX(width() - 1);
           }
           if (mouse_p.y() < 0)
           {
               mouse_p.setY(0);
           }
           else if (mouse_p.y() > height() - 1)
           {
               mouse_p.setY(height() - 1);
           }

           if (curEditType == DrawSelection)
           {
               //根据按下时位置和当前位置确定一个选区
               selection = QRect(pressPos, mouse_p);
           }
           else if (curEditType == MoveSelection)
           {
               //移动选区
               selection.moveTopLeft(mousePos - tempPos);
				
			   //select1.moveTopLeft(mousePos - tempPos1);
               //限制范围在可视区域
               if (selection.left() < 73)
               {
                   //selection.moveLeft(0);
				   selection.moveLeft(73);
               }
               else if (selection.right() > width() - 48)
               {
                   selection.moveRight(width() - 48);
				   //selection.moveRight(450);
               }
               if (selection.top() < 37)
               {
                   selection.moveTop(37);
               }
               else if (selection.bottom() > height() - 53)
               {
                   //selection.moveBottom(height() - 1);
				   selection.moveBottom(height() - 53);
               }
           }
           else if (curEditType == EditSelection)
           {
               //拉伸选区边界
               int position = curPosition;
               if (position & AtLeft)
               {
                   if (mouse_p.x() < selection.right())
                   {
                       selection.setLeft(mouse_p.x());
                   }
                   else
                   {
                       selection.setLeft(selection.right() - 10);
                   }
               }
               else if (position & AtRight)
               {
                   if (mouse_p.x() > selection.left())
                   {
                       selection.setRight(mouse_p.x());
                   }
                   else
                   {
                       selection.setRight(selection.left() + 10);
                   }
               }
           }
       }
       else
       {
           setCurPosition(calcPosition(mousePos));
       }

	   if (pressFlag1)
	   {
		   if (curEditType1 == PressInside)
		   {
			   if (QPoint(pressPos - mousePos).manhattanLength() > 3)
			   {
				   curEditType1 = MoveSelection;
			   }
		   }

		   QPoint mouse_p = mousePos;
		   //限制范围在可视区域
		   if (mouse_p.x() < 0)
		   {
			   mouse_p.setX(0);
		   }
		   else if (mouse_p.x() > width() - 1)
		   {
			   mouse_p.setX(width() - 1);
		   }
		   if (mouse_p.y() < 0)
		   {
			   mouse_p.setY(0);
		   }
		   else if (mouse_p.y() > height() - 1)
		   {
			   mouse_p.setY(height() - 1);
		   }

		   if (curEditType1 == DrawSelection)
		   {
			   //根据按下时位置和当前位置确定一个选区
			   select1 = QRect(pressPos, mouse_p);
		   }
		   else if (curEditType1 == MoveSelection)
		   {
			   //移动选区
			   select1.moveTopLeft(mousePos - tempPos1);

			   //限制范围在可视区域
			   if (select1.left() < 73)
			   {
				   select1.moveLeft(73);
			   }
			   else if (select1.right() > width() - 48)
			   {
				   select1.moveRight(width() - 48);
			   }
			   if (select1.top() < 37)
			   {
				   select1.moveTop(37);
			   }
			   else if (select1.bottom() > height() - 53)
			   {
				   select1.moveBottom(height() - 53);
			   }
		   }
		   else if (curEditType1 == EditSelection)
		   {
			   //拉伸选区边界
			   int position = curPosition1;
			   if (position & AtLeft)
			   {
				   if (mouse_p.x() < select1.right())
				   {
					   select1.setLeft(mouse_p.x());
				   }
				   else
				   {
					   select1.setLeft(select1.right() - 10);
				   }
			   }
			   else if (position & AtRight)
			   {
				   if (mouse_p.x() > select1.left())
				   {
					   select1.setRight(mouse_p.x());
				   }
				   else
				   {
					   select1.setRight(select1.left() + 10);
				   }
			   }
		   }
	   }
	   else
	   {
		   setCurPosition1(calcPosition1(mousePos));
	   }
       update();
}

void mycharts::mouseReleaseEvent(QMouseEvent *event)
{
    QChartView::mouseReleaseEvent(event);
    //event->accept();
       mousePos = event->pos();
       pressFlag = false;
	   pressFlag1 = false;
       if (curEditType != EditNone)
       {
           //编辑结束后判断是否小于最小宽度,是则取消选区
           if (curEditType == DrawSelection)
           {
               selection = selection.normalized();
               if (selection.width() < Min_Width || selection.height() < Min_Width)
               {
                   hasSelection = false;
               }
           }
           else if (curEditType == MoveSelection)
           {
           }
           else if (curEditType == EditSelection)
           {
              /* if (selection.width() < Min_Width || selection.height() < Min_Width)
               {
                   hasSelection = false;
               }*/
           }
           curEditType = EditNone;
       }
	   setCurPosition(calcPosition(mousePos));

	   if (curEditType1 != EditNone)
	   {
		   if (curEditType1 == DrawSelection)
		   {
			   select1 = select1.normalized();
			   if (select1.width() < Min_Width || select1.height() < Min_Width)
			   {
				   hasSelection = false;
			   }
		   }
		   else if (curEditType1 == MoveSelection)
		   {
		   }
		   else if (curEditType1 == EditSelection)
		   {
			   /* if (selection.width() < Min_Width || selection.height() < Min_Width)
			   {
			   hasSelection = false;
			   }*/
		   }
		   curEditType1 = EditNone;
	   }
	   setCurPosition1(calcPosition1(mousePos));
       update();
}

mycharts::AreaPosition mycharts::calcPosition(const QPoint &pos)
{
    //一条线太窄,不好触发,增加判断范围又会出现边界太近时交叠在一起
       //目前的策略是从右下开始判断,左上的优先级更低一点
       static const int check_radius = 3;
       int position = AreaPosition::Outside;
       QRect check_rect = selection.adjusted(-check_radius, -check_radius, check_radius-1, check_radius-1);
       //无选区,或者不在选区判定范围则返回outside
       if (!hasSelection || !check_rect.contains(pos))
       {
           return (mycharts::AreaPosition)position;
       }
       //判断是否在某个边界上

       if (std::abs(pos.x() - selection.right()) < check_radius)
       {
           position |= AreaPosition::AtRight;
       }
       else if (std::abs(pos.x() - selection.left()) < check_radius)
       {
           position |= AreaPosition::AtLeft;
       }
       if (std::abs(pos.y() - selection.bottom()) < check_radius)
       {
           position |= AreaPosition::AtBottom;
       }
       else if (std::abs(pos.y() - selection.top()) < check_radius)
       {
           position |= AreaPosition::AtTop;
       }
       //没在边界上就判断是否在内部
       if (position == AreaPosition::Outside && selection.contains(pos))
       {
           position = AreaPosition::Inside;
       }
       return (mycharts::AreaPosition)position;
}

mycharts::AreaPosition mycharts::calcPosition1(const QPoint & pos)
{
	//一条线太窄,不好触发,增加判断范围又会出现边界太近时交叠在一起
	//目前的策略是从右下开始判断,左上的优先级更低一点
	static const int check_radius = 3;
	int position = AreaPosition::Outside;
	QRect check_rect = select1.adjusted(-check_radius, -check_radius, check_radius - 1, check_radius - 1);
	//无选区,或者不在选区判定范围则返回outside
	if (!hasSelection || !check_rect.contains(pos))
	{
		return (mycharts::AreaPosition)position;
	}
	//判断是否在某个边界上

	if (std::abs(pos.x() - select1.right()) < check_radius)
	{
		position |= AreaPosition::AtRight;
	}
	else if (std::abs(pos.x() - select1.left()) < check_radius)
	{
		position |= AreaPosition::AtLeft;
	}
	if (std::abs(pos.y() - select1.bottom()) < check_radius)
	{
		position |= AreaPosition::AtBottom;
	}
	else if (std::abs(pos.y() - select1.top()) < check_radius)
	{
		position |= AreaPosition::AtTop;
	}
	//没在边界上就判断是否在内部
	if (position == AreaPosition::Outside && select1.contains(pos))
	{
		position = AreaPosition::Inside;
	}
	return (mycharts::AreaPosition)position;
}

void mycharts::setCurPosition(mycharts::AreaPosition position)
{
    if (position != curPosition)
    {
        curPosition = position;
        updateCursor();
    }
}

void mycharts::setCurPosition1(AreaPosition position)
{
	if (position != curPosition1)
	{
		curPosition1 = position;
		updateCursor1();
	}
}

void mycharts::updateCursor()
{
    switch (curPosition)
      {
      case AtLeft:
      case AtRight:
          setCursor(Qt::SizeHorCursor);
          break;
      case AtTop:
      case AtBottom:
          setCursor(Qt::SizeVerCursor);
          break;
      case AtTopLeft:
      case AtBottomRight:
          setCursor(Qt::SizeFDiagCursor);
          break;
      case AtTopRight:
      case AtBottomLeft:
          setCursor(Qt::SizeBDiagCursor);
          break;
      default:
          setCursor(Qt::ArrowCursor);
          break;
      }
}

void mycharts::updateCursor1()
{
	switch (curPosition1)
	{
	case AtLeft:
	case AtRight:
		setCursor(Qt::SizeHorCursor);
		break;
	case AtTop:
	case AtBottom:
		setCursor(Qt::SizeVerCursor);
		break;
	case AtTopLeft:
	case AtBottomRight:
		setCursor(Qt::SizeFDiagCursor);
		break;
	case AtTopRight:
	case AtBottomLeft:
		setCursor(Qt::SizeBDiagCursor);
		break;
	default:
		setCursor(Qt::ArrowCursor);
		break;
	}
}

  • 3
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

讳疾忌医丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值