参考QAbstractItemView,QTableView:https://blog.csdn.net/lsfreeing/article/details/78269033
参考Qt模型/视图原理(4):自定义视图(QAbstractItemView):https://blog.csdn.net/hyongilfmmm/article/details/83099833
参考Qt官方例子:Chart Example
参考Qt QStandardItemModel用法(超级详细):http://c.biancheng.net/view/1869.html
首先说一下自己对于QAbstractItemView的理解:
1.为什么要使用这个类
为了使模型和视图分开,在我理解模型就是数据,视图就是在屏幕上显示需要给用户看的数据,以一种更直观更容易理解的样式呈现。当然也可以自己定义结构体来管理一份数据来进行刷新。但是这里qt提供了这样一个类专门来干这件事,QAbstractItemModel类来管理模型(我觉得的就是数据),QAbstractItemView类进行视图绘制,一旦模型数据改变,视图会自动根据模型数据进行改变。
2.使用这个类干什么
说的不一定对。我把该类看作是一块区域,在这个区域里你可以用qt的画笔painter来绘制你想要呈现的任何图形样式,当然需要模型数据进行驱动。
以下我觉得比较标准还比较容易理解的定义:
自定义视图(QAbstractItemView),是每一个使用QAbstractItemModel的标准视图的基类。QAbstractItemView是抽象类从而不能实例化。通过信号与槽机制,提供一个标准的接口与model进行交互操作,使子类能够在模型的变化中保持最新。这个类为键盘和鼠标导航、窗口滚动、项编辑和选择提供了标准支持。
KDGantt.h
#pragma once
#include <QMainWindow>
QT_BEGIN_NAMESPACE
class QAbstractItemModel;
class QAbstractItemView;
class QItemSelectionModel;
QT_END_NAMESPACE
class KDGantt : public QMainWindow
{
Q_OBJECT
public:
KDGantt();
private:
void setupModel();
void setupViews();
private:
QAbstractItemModel* gantt_model;
QAbstractItemView* gantt_chart;
};
KDGantt.cpp
#pragma execution_character_set("utf-8")
#include <QtWidgets>
#include "KDGantt.h"
#include "GanttView.h"
KDGantt::KDGantt()
{
setupModel();
setupViews();
gantt_model->insertRows(0, 1, QModelIndex());
gantt_model->setData(gantt_model->index(0, 0, QModelIndex()), "1");
gantt_model->setData(gantt_model->index(0, 1, QModelIndex()), "3:00");
gantt_model->setData(gantt_model->index(0, 2, QModelIndex()), "14:00");
gantt_model->setData(gantt_model->index(0, 3, QModelIndex()), "8:00");
}
void KDGantt::setupModel()
{
gantt_model = new QStandardItemModel(0, 4, this);
gantt_model->setHeaderData(0, Qt::Horizontal, tr("id"));
gantt_model->setHeaderData(1, Qt::Horizontal, tr("起始时间"));
gantt_model->setHeaderData(2, Qt::Horizontal, tr("终止时间"));
gantt_model->setHeaderData(3, Qt::Horizontal, tr("进程"));
}
void KDGantt::setupViews()
{
QSplitter* splitter = new QSplitter;
QTableView* table = new QTableView;
gantt_chart = new GanttView;
splitter->addWidget(table);
splitter->addWidget(gantt_chart);
splitter->setStretchFactor(0, 0);
splitter->setStretchFactor(1, 1);
table->setModel(gantt_model);
table->setModel(gantt_model);
QHeaderView* headerView = table->horizontalHeader();
headerView->setStretchLastSection(true);
setCentralWidget(splitter);
}
GanttView.h
#pragma once
#include <QAbstractItemView>
class GanttView : public QAbstractItemView
{
Q_OBJECT
public:
GanttView(QWidget *parent = 0);
~GanttView();
// 用于计算项目所占据的矩形(即位置和大小)
// 该函数在初次运行时便会由Qt调用,调用次数依模型而定
// 参数index包含模型的索引,index会在调用时循环传递
virtual QRect visualRect(const QModelIndex& index) const override;
// 返回鼠标光标所在位置的项目的索引
// 在点击鼠标时Qt会调用,参数point包含了鼠标光标的坐标位置(视图坐标)
virtual QModelIndex indexAt(const QPoint& point) const override;
//以下3个函数用于计算视图的滚动,不需要滚动不需要实现
virtual void scrollTo(const QModelIndex& index, ScrollHint hint = EnsureVisible) override;
virtual int horizontalOffset() const override;
virtual int verticalOffset() const override;
protected:
//用于处理键盘按键,不处理不用实现
virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override;
//是否隐藏项目,不需要直接返回0
virtual bool isIndexHidden(const QModelIndex& index) const override;
//以下2个函数主要用于处理对项目的选择,当选择视图中的项目时,Qt才会调用他们。
//当不需要选择项目时,以下两个函数可以不用实现。
//参数rect包含了所选项目的矩形(位置和大小,使用视图坐标)
//参数flags包含了选择项目时的选择标志。
virtual void setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command) override;
//此函数用于计算所有被选择的项目占据的区域(即位置和大小)。
//参数s包含了所选择的项目的范围。
virtual QRegion visualRegionForSelection(const QItemSelection& selection) const override;
void paintEvent(QPaintEvent* event) override;
void resizeEvent(QResizeEvent* event) override;
//根据滚动条设置偏移
virtual void scrollContentsBy(int dx, int dy) override;
private:
//缩小后垂直水平滚动条计算大小
void updateGeometries() override;
//转换时间
//int changeTime(QString _time);
private:
int time_wide; //时间轴宽度
int time_high; //时间轴高度
int total_size; //总尺寸
};
GanttView.cpp
#include "GanttView.h"
#include <QtWidgets>
GanttView::GanttView(QWidget *parent)
: QAbstractItemView(parent)
{
//水平垂直滚动条
horizontalScrollBar()->setRange(0, 0);
verticalScrollBar()->setRange(0, 0);
total_size = 1600;
time_wide = 1440;
time_high = 30;
}
GanttView::~GanttView()
{
}
QRect GanttView::visualRect(const QModelIndex& index) const
{
return QRect();
}
void GanttView::scrollTo(const QModelIndex& index, ScrollHint hint /*= EnsureVisible*/)
{
return;
}
QModelIndex GanttView::indexAt(const QPoint& point) const
{
return QModelIndex();
}
QModelIndex GanttView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
{
return QModelIndex();
}
int GanttView::horizontalOffset() const
{
return horizontalScrollBar()->value();
}
int GanttView::verticalOffset() const
{
return verticalScrollBar()->value();
}
bool GanttView::isIndexHidden(const QModelIndex& index) const
{
return 0;
}
void GanttView::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command)
{
return;
}
QRegion GanttView::visualRegionForSelection(const QItemSelection& selection) const
{
return QRegion();
}
void GanttView::paintEvent(QPaintEvent* event)
{
QPainter time_axis_painter(viewport());
//初始偏移量
time_axis_painter.translate(80, 30);
//根据滚动条变化设置偏移量
time_axis_painter.translate(0 - horizontalScrollBar()->value(), 0 - verticalScrollBar()->value());
QFont axis_font("宋体", 5);
time_axis_painter.setFont(axis_font);
QPen time_axis_pen;
time_axis_pen.setWidth(1.5);
time_axis_pen.setColor(QColor(0, 0, 0));
//画时间轴
time_axis_painter.setPen(time_axis_pen);
//画时间轴矩形
time_axis_painter.drawRect(0, 0, time_wide, time_high);
//画时间轴大刻度线and标尺
int average = time_wide / 24;
int interval = average;
int min_interval = interval / 10;
time_axis_painter.drawText(0, time_high + 15, "0:00");
for (int min_axis=0;min_axis<10;min_axis++)
{
time_axis_painter.drawLine(QPoint(min_interval * min_axis, time_high - 5), QPoint(min_interval * min_axis, time_high));
}
for (int i=1;i<24;i++)
{
time_axis_painter.drawLine(QPoint(interval, 0), QPoint(interval, time_high));
for (int min_axis=0;min_axis<10;min_axis++)
{
time_axis_painter.drawLine(QPoint(interval + (min_interval * min_axis), time_high - 5), QPoint(interval + (min_interval * min_axis), time_high));
}
time_axis_painter.drawText(interval, time_high + 15, QString::number(i) + ":00");
interval = interval + average;
}
time_axis_painter.drawText(interval, time_high + 15, "24:00");
interval = 0;
//画进度
//刻度精度到分
// int scale = time_wide / 24 / 60;
// int height_change = 10;
// int row,column;
// for (row = 0; row < model()->rowCount(); ++row) {
// int begin_time_int = 0;
// for (column=1;column<4;++column)
// {
// QModelIndex index = model()->index(row, column,rootIndex());
// QString begin_time = model()->data(index).toString();
// QStringList begin_time_list = begin_time.split(":");
// int begin_hour = begin_time_list[0].toInt()*60;
// int begin_minute = begin_time_list[1].toInt();
// begin_time_int = begin_hour + begin_minute;
// }
// time_axis_painter.drawRect(begin_time_int, 30+height_change* (row+1), time_wide, 30);
// }
}
void GanttView::resizeEvent(QResizeEvent* event)
{
updateGeometries();
}
void GanttView::scrollContentsBy(int dx, int dy)
{
//界面根据滚动条动起来
viewport()->scroll(dx, dy);
}
void GanttView::updateGeometries()
{
//设置滚动条能显示到的最大范围
horizontalScrollBar()->setPageStep(viewport()->width());
horizontalScrollBar()->setRange(0, qMax(0, total_size - viewport()->width()));
verticalScrollBar()->setPageStep(viewport()->height());
verticalScrollBar()->setRange(0, qMax(0, total_size - viewport()->height()));
}
main.cpp
#include "KDGantt.h"
#include <QtWidgets/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
KDGantt w;
w.show();
return a.exec();
}