首页 > 代码库 > 【Qt5开发及实例】24、数据柱形图显示

【Qt5开发及实例】24、数据柱形图显示

数据柱形图显示

1、我们首先把这个这个视图的表格部分表示出来

mainwindow.h

/**
* 书本:【Qt5开发及实例】
* 功能:数据柱形图显示,这个类是表格显示
* 文件:mainwindow.h
* 时间:2015年1月28日18:50:54
* 作者:cutter_point
*/
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QStandardItemModel>
#include <QSplitter>
#include <QMenu>
#include <QMenuBar>
#include <QTableView>
#include <QString>

#include "histogramview.h"

class MainWindow : public QMainWindow
{
  Q_OBJECT

public:
  MainWindow(QWidget *parent = 0);
  ~MainWindow();
  //创建文件里面的打开动作
  void createAction();
  //创建菜单栏
  void createMenu();
  //设置模型
  void setupModel();
  //设置显示视图
  void setupView();
  //读取打开的文件
  void openFile(QString);

public slots:
  //打开文件读取文件的槽函数
  void slotOpen();

private:
  //私有的控件成员
  QMenu *fileMenu;    //文件菜单
  QAction *openAct;   //打开文件事件

  QStandardItemModel *model;      //界面模型
  QTableView *table;    //表格视图
  QSplitter *splitter;      //界面的分割布局

  HistogramView *histogram;

};

#endif // MAINWINDOW_H

mainwindow.cpp

/**
* 书本:【Qt5开发及实例】
* 功能:数据柱形图显示
* 文件:mainwindow.h
* 时间:2015年1月28日18:50:54
* 作者:cutter_point
*/
#include "mainwindow.h"

#include <QFileDialog>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)   //构造函数
  : QMainWindow(parent)
{
  createAction();   //生成相应的事件
  createMenu();   //生成菜单
  setupModel();   //设置模型
  setupView();    //显示视图

  //设置标题和界面大小
  this->setWindowTitle(tr("cutter_point"));
  this->resize(600, 600);
}

MainWindow::~MainWindow()
{

}

//创建文件里面的打开动作
//void createAction();
void MainWindow::createAction()
{
  //创建我们的打开事件
  openAct = new QAction(tr("open"), this);

  //连接上打开文件
  connect(openAct, SIGNAL(triggered()), this, SLOT(slotOpen()));    //点击选择这个事件连接上这个槽函数
}

//创建菜单栏
//void createMenu();
void MainWindow::createMenu()
{
  //设置文件菜单
  fileMenu = new QMenu(tr("file"), this);
  fileMenu->addAction(openAct);   //为这个菜单栏加入一个选项
  this->menuBar()->addMenu(fileMenu);   //加入到主窗口的菜单行中
}

//设置模型
//void setupModel();
void MainWindow::setupModel()
{
  model = new QStandardItemModel(4, 4, this);   //在当前的窗口中创建一个4行4列的标准模型
  //头标题就是4个,部门,男,女,退休
  model->setHeaderData(0, Qt::Horizontal, tr("department"));  //部门
  model->setHeaderData(1, Qt::Horizontal, tr("male"));    //男
  model->setHeaderData(2, Qt::Horizontal, tr("female"));    //女
  model->setHeaderData(3, Qt::Horizontal, tr("retirement"));    //退休
}

//设置显示视图
//void setupView();
void MainWindow::setupView()
{
  //设置好表格视图
  table = new QTableView;
  table->setModel(model);   //设置这个表格视图的模型

  QItemSelectionModel *selectionModel = new QItemSelectionModel(model);
  table->setSelectionModel(selectionModel);

  splitter = new QSplitter;   //分割界面
  splitter->setOrientation(Qt::Vertical);   //垂直分割

//  histogram = new HistogramView;
  histogram = new HistogramView(splitter);

  histogram->setModel(model); //添加到这个模型中

  histogram->setSelectionModel(selectionModel);

  splitter->addWidget(table);
  splitter->addWidget(histogram);

  this->setCentralWidget(splitter);

  connect(selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), table, SLOT(selectionChanged(QItemSelection,QItemSelection)));
  connect(selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), histogram, SLOT(selectionChanged(QItemSelection,QItemSelection)));

}

//打开文件读取文件的槽函数
//void slotOpen();
void MainWindow::slotOpen()
{
  QString name;     //文件名
  name = QFileDialog::getOpenFileName(this, "open", ".", "file(*.txt; *.doc)");    //设置文件打开对话框

  if(!name.isEmpty())   //取得文件
    {
      //打开文件,读取文件里面的数据
      this->openFile(name);
    }

}

//读取打开的文件
//  void openFile();
void MainWindow::openFile(QString path)
{
  //如果得到的路径不为空
  if(!path.isEmpty())
    {
      QFile file(path);   //取得文件
      qDebug()<<path;   //看看路径的格式

      if(file.open(QFile::ReadOnly | QFile::Text))    //打开文件里面只有文本,并且是只读格式,表示打开成功
        {
          QTextStream stream(&file);      //文件读取流
          QString line;     //保存一行数据

          model->removeRows(0, model->rowCount(QModelIndex()), QModelIndex());    //删除从第一个index到第二个index的行,总之先清除各行

          int row = 0;    //第0行

          do
            {
              line = stream.readLine();   //读取一行
              //如果读取到了数据
              if(!line.isEmpty())
                {
                  model->insertRow(row, QModelIndex());    //把第row行数据插入,那个QModelIndex()是个什么东西???
                  QStringList pieces = line.split(",", QString::SkipEmptyParts);    //把这行数据按“,”分开,然后空白的部分不要
                  model->setData(model->index(row, 0, QModelIndex()), pieces.value(0));   //插入一行的第一个数据
                  model->setData(model->index(row, 1, QModelIndex()), pieces.value(1));   //插入一行的第一个数据
                  model->setData(model->index(row, 2, QModelIndex()), pieces.value(2));   //插入一行的第一个数据
                  model->setData(model->index(row, 3, QModelIndex()), pieces.value(3));   //插入一行的第一个数据

                  //下一行
                  row++;
                }//if(!line.isEmpty())
            }while(!line.isEmpty());      //当读取到空的数据的时候就退出

          file.close();   //关闭文件流

        }//if(file.open(QFile::ReadOnly | QFile::Text))

    }// if(!path.isEmpty())
}//void MainWindow::openFile(QString path)



2、然后我们开始绘画条状图

histogramview.h

/**
* 书本:【Qt5开发及实例】
* 功能:数据柱形图显示,这个类是柱形图
* 文件:histogramview.h
* 时间:2015年1月28日20:23:40
* 作者:cutter_point
*/
#ifndef HISTOGRAMVIEW_H
#define HISTOGRAMVIEW_H

#include <QAbstractItemView>
#include <QItemSelectionModel>
#include <QRegion>
#include <QMouseEvent>

class HistogramView : public QAbstractItemView
{
  Q_OBJECT
public:
  explicit HistogramView(QWidget *parent = 0);
  //这里有些继承来的纯虚函数,可以实现,但必须声明
//  virtual QRect visualRect(const QModelIndex &index) const = 0;
//  virtual void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) = 0;
//  virtual QModelIndex indexAt(const QPoint &point) const = 0;
  QRect visualRect(const QModelIndex &index) const;
  void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible);
  QModelIndex indexAt(const QPoint &point) const;

  //绘制柱形图的函数
  void paintEvent(QPaintEvent *);
  //为selections赋初值
  // QAbstractItemView interface
  void setSelectionModel(QItemSelectionModel *selectionModel);
  //当鼠标在按下的时候调用setSelection函数,确定鼠标点击点所在的区域,设置选择项
  void mousePressEvent(QMouseEvent *event);
  //选中区域是属于什么
  QRegion itemRegion(QModelIndex index);


signals:

public slots:

protected slots:
  void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
  void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);

protected:
    QModelIndex moveCursor(CursorAction cursorAction,
                                   Qt::KeyboardModifiers modifiers);

    int horizontalOffset() const;
    int verticalOffset() const;

    bool isIndexHidden(const QModelIndex &index) const;

    void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command);
    QRegion visualRegionForSelection(const QItemSelection &selection) const;
  //  virtual QModelIndex moveCursor(CursorAction cursorAction,
  //                                 Qt::KeyboardModifiers modifiers) = 0;

  //  virtual int horizontalOffset() const = 0;
  //  virtual int verticalOffset() const = 0;

  //  virtual bool isIndexHidden(const QModelIndex &index) const = 0;

  //  virtual void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command) = 0;
  //  virtual QRegion visualRegionForSelection(const QItemSelection &selection) const = 0;

private:
  QItemSelectionModel *selections;
  QList<QRegion> MRegionList;
  QList<QRegion> FRegionList;
  QList<QRegion> SRegionList;

};

#endif // HISTOGRAMVIEW_H

histogramview.cpp

/**
* 书本:【Qt5开发及实例】
* 功能:数据柱形图显示,这个类是柱形图
* 文件:histogramview.cpp
* 时间:2015年1月28日20:36:24
* 作者:cutter_point
*/
#include "histogramview.h"

#include <QPainter>   //绘画工具
#include <QDebug>

HistogramView::HistogramView(QWidget *parent) :
  QAbstractItemView(parent)
{
}

//绘制柱形图的函数
//void paintEvent(QPaintEvent *);
void HistogramView::paintEvent(QPaintEvent *)
{
  QPainter painter(viewport());   //这个就是在这个viewport范围内建立一个绘画对象,这个viewport就是给定的范围,大小暂时不知,反正就是一个地方
  painter.setPen(Qt::yellow);     //来一只黄色的笔

  int x0 = 40;
  int y0 = 250;     //来了,坐标0号

  //y坐标轴
  painter.drawLine(x0,y0,40,30);    //高
  painter.drawLine(38,32,40,30);    //这是两个箭头
  painter.drawLine(40,30,42,32);
  painter.drawText(20,30,tr("人数"));   //坐标轴后面的给个文字

  for(int i=1; i<5; i++)      //y坐标的刻度
    {
        painter.setPen(Qt::black);     //来一只黑色的笔
        painter.drawLine(x0, i*50, 50, i*50);
//        qDebug()<<"=======Chinese????"<<i;
        painter.drawText(20, (5-i)*50, tr("%1").arg(i*5));
    }

  //x 坐标轴
  painter.setPen(Qt::yellow);     //来一只黄色的笔
  painter.drawLine(x0,y0,540,250);
  painter.drawLine(538,248,540,250);
  painter.drawLine(540,250,538,252);
  painter.drawText(545,250,tr("部门"));

  //接下来一个一个地画图形
  //部门
  int posD = x0 + 20;   //重这里开始画,后面可以看到相当于重表示男人数的柱子下面开始写
  int row;    //标记写到了第几行
  for(row = 0; row < model()->rowCount(this->rootIndex()); ++row)   //model() 返回已经呈现的视图
    {//rootIndex统计最开始的那个索引
//      qDebug()<<this->rootIndex();    //这个根索引
      QModelIndex index = model()->index(row, 0, rootIndex());    //得到第row行,第1列,第0个的索引
      QString dep = model()->data(index).toString();    //得到这个索引的数据

      painter.drawText(posD, y0 + 20, dep);   //dep就是刚得到的部门信息
      posD += 50;   //下一个

    }

  //男
//  qDebug()<<"111111";
  int posM=x0+20;
  for(row=0; row<model()->rowCount(rootIndex()); ++row)
  {
      QModelIndex index=model()->index(row,1,rootIndex());    //第二列
      int male=model()->data(index).toDouble();   //数值转化
//      qDebug()<<index;


      int width=10;   //柱形图柱子的宽度
//      qDebug()<<"1111111";      //注意这里selection还没有初值,也就是还没有实例对象,没有模型对象
      if(selections->isSelected(index))   //用不同的颜色区别选择的部分和未选择的部分,index是这一行的索引
        {
          qDebug()<<"1111111ifififififif";
          painter.setBrush(QBrush(Qt::black,Qt::Dense3Pattern));   //设置画刷
        }
      else
        {
//          qDebug()<<"1111111elseelseelseelse";
          painter.setBrush(Qt::blue);
        }
//      qDebug()<<"22222";
      painter.drawRect(QRect(posM,y0-male*10,width,male*10));   //画柱子,宽度已设置为10

      QRegion regionM(posM,y0-male*10,width,male*10);   //剪裁一个区域的大小记录下来
      MRegionList<<regionM;   //把占据的区域保存下来给后面使用

      posM+=50;
  }
//  qDebug()<<"22222";

  //女
//  qDebug()<<"女1";
  int posF=x0+30;     //这里是吧位置设置到离上面为10 的地方
  for(row=0; row<model()->rowCount(rootIndex()); ++row)
  {
      QModelIndex index=model()->index(row,2,rootIndex());//第三列
      int female=model()->data(index).toDouble();

      int width=10;
      if(selections->isSelected(index)) //用不同的颜色区别选择的部分和未选择的部分,index是这一行的索引
          painter.setBrush(QBrush(Qt::black,Qt::Dense3Pattern));
      else
          painter.setBrush(Qt::red);    //没选中就是红色

      painter.drawRect(QRect(posF,y0-female*10,width,female*10));

      QRegion regionF(posF,y0-female*10,width,female*10); //剪裁一个区域的大小记录下来
      FRegionList<<regionF; //把占据的区域保存下来给后面使用

      posF+=50;
  }
//  qDebug()<<"女2";

  //退休
//  qDebug()<<"退休1";
  int posS=x0+40;
  for(row=0;row<model()->rowCount(rootIndex());row++)
  {
      QModelIndex index=model()->index(row,3,rootIndex());
      int retire=model()->data(index).toDouble();

      int width=10;
      if(selections->isSelected(index))  //用不同的颜色区别选择的部分和未选择的部分,index是这一行的索引
          painter.setBrush(QBrush(Qt::black,Qt::Dense3Pattern));
      else
          painter.setBrush(Qt::green);

      painter.drawRect(QRect(posS,y0-retire*10,width,retire*10));

      QRegion regionS(posS, y0-retire*10, width, retire*10);   //就是把在(posS, y0-retire)开始高度retire*10和宽度10的区域
      SRegionList<<regionS;

      posS+=50;
  }//for(row=0;row<model()->rowCount(rootIndex());row++)
//  qDebug()<<"退休2";


}//void HistogramView::paintEvent(QPaintEvent *)

//当模型的数据改变的时候,调用绘图设备的update函数更新
void HistogramView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
  QAbstractItemView::dataChanged(topLeft, bottomRight);
  this->viewport()->update();   //在这个基础上改变更新
}

//设置selections的初值
void HistogramView::setSelectionModel(QItemSelectionModel *selectionModel)
{
  selections = selectionModel;
}

QRect HistogramView::visualRect(const QModelIndex &index)const
{
  QRect *p = new QRect();
  return *p;
}

void HistogramView::scrollTo(const QModelIndex &index,ScrollHint){}

QModelIndex HistogramView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
{
  return QModelIndex();
}

int HistogramView::horizontalOffset()const
{
  return 0;
}

int HistogramView::verticalOffset()const
{
  return 0;
}

bool HistogramView::isIndexHidden(const QModelIndex &index)const
{
  return true;
}

QRegion HistogramView::visualRegionForSelection(const QItemSelection &selection)const
{
  return QRegion();
}


//选项的更新
void HistogramView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
  viewport()->update();     //更新数据
}

 //当鼠标在按下的时候调用setSelection函数,确定鼠标点击点所在的区域,设置选择项
void HistogramView::mousePressEvent(QMouseEvent *event)
{
  QAbstractItemView::mousePressEvent(event);    //视图层的鼠标点击事件获取
  qDebug()<<"mouse";
  setSelection(QRect(event->pos().x(), event->pos().y(), 1, 1), QItemSelectionModel::SelectCurrent);    //获取当前选择的那个区域
}

//返回选择到的数值的区域
QRegion HistogramView::itemRegion(QModelIndex index)
{
  QRegion region;
  if(index.column() == 1)   //第二列
    {
      region = MRegionList[index.row()];    //得到点击到的当前行号对应的男生数值区域
    }
  if(index.column() == 2)   //第三列
    {
      region = FRegionList[index.row()];    //得到点击到的当前行号对应的女生数值区域
    }
  if(index.column() == 3)   //第四列
    {
      region = SRegionList[index.row()];    //得到点击到的当前行号对应的退休生数值区域
    }

  return region;
}

//设置选择的区域
void HistogramView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
{
    int rows = model()->rowCount(rootIndex());
    int columns = model()->columnCount(rootIndex());
    QModelIndex selectedIndex;

    for (int row=0; row<rows; ++row)
    {
        for (int column=1; column<columns; ++column)
        {
            QModelIndex index=model()->index(row, column, rootIndex());   //得到选择中的位置的索引
            QRegion region=itemRegion(index);   //得到这个区域

            if (!region.intersected(rect).isEmpty())    //没有重合的部分
                selectedIndex = index;
        }
    }

    if(selectedIndex.isValid())   //里面的数据不为空
        selections->select(selectedIndex, command);
    else
    {
        qDebug()<<"noindex";
        QModelIndex noIndex;    //空的索引
        selections->select(noIndex, command);
    }
}

QModelIndex HistogramView::indexAt(const QPoint &point)const
{
    QPoint newPoint(point.x(),point.y());
    QRegion region;
    foreach(region,MRegionList)				// 男 列
    {
        if (region.contains(newPoint))
        {
            int row = MRegionList.indexOf(region);
            QModelIndex index = model()->index(row,1,rootIndex());
            return index;
        }
    }
    foreach(region,FRegionList)				// 女 列
    {
        if (region.contains(newPoint))
        {
            int row = FRegionList.indexOf(region);
            QModelIndex index = model()->index(row,2,rootIndex());
            return index;
        }
    }
    foreach(region,SRegionList)				// 合计 列
    {
        if (region.contains(newPoint))
        {
            int row = SRegionList.indexOf(region);
            QModelIndex index = model()->index(row,3,rootIndex());
            return index;
        }
    }
    return QModelIndex();
}

main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
  QApplication a(argc, argv);
  MainWindow w;
  w.show();

  return a.exec();
}

3、效果展示

技术分享



没错,我就是信管专业的,就是信息管理与信息系统






【Qt5开发及实例】24、数据柱形图显示