Qt绘制甘特图,网上找了很久,只找到kdChart,编译很简单,我是Qt 5.5.0 + VS2013,直接编译就行,kdChart有甘特图例子:
但是和项目设计有点出入,所以自定义部分显示:
1.左边的是QTreeView,并为其添加委托,控件变化主要是通过QStandardItem->setData()来区别,这没什么说的,主要记录修改颜色和获取甘特图item拖动事件
(1).修改颜色(可实现每个item的颜色都不一样):
这是设置每个item的颜色:
topitem->setData(249, KDGantt::ItemColor_R);
topitem->setData(171, KDGantt::ItemColor_G);
topitem->setData(82, KDGantt::ItemColor_B);
kdChart中,颜色的修改是:
void ItemDelegate::paintGanttItem( QPainter* painter,
const StyleOptionGanttItem& opt,
const QModelIndex& idx )
函数修改:
void ItemDelegate::paintGanttItem( QPainter* painter,
const StyleOptionGanttItem& opt,
const QModelIndex& idx )
{
if ( !idx.isValid() ) return;
const ItemType typ = static_cast( idx.model()->data( idx, ItemTypeRole ).toInt() );
const QString& txt = opt.text;
QRectF itemRect = opt.itemRect;
QRectF boundingRect = opt.boundingRect;
boundingRect.setY( itemRect.y() );
boundingRect.setHeight( itemRect.height() );
painter->save();
int color_r = idx.model()->data( idx, ItemColor_R ).toInt();
int color_g = idx.model()->data( idx, ItemColor_G ).toInt();
int color_b = idx.model()->data( idx, ItemColor_B ).toInt();
QColor itemColor(color_r, color_g, color_b);
QPen pen(itemColor);
if ( opt.state & QStyle::State_Selected ) pen.setWidth( 2*pen.width() );
painter->setPen( pen );
painter->setBrush( QBrush(Qt::white, Qt::Dense1Pattern) );
bool drawText = true;
qreal pw = painter->pen().width()/2.;
switch ( typ ) {
case TypeTask:
if ( itemRect.isValid() ) {
// TODO
qreal pw = painter->pen().width()/2.;
pw-=1;
QRectF r = itemRect;
r.translate( 0., r.height()/6. );
r.setHeight( 2.*r.height()/3. );
painter->setBrushOrigin( itemRect.topLeft() );
painter->save();
painter->translate( 0.5, 0.5 );
painter->drawRect( r );
bool ok;
qreal completion = idx.model()->data( idx, KDGantt::TaskCompletionRole ).toReal( &ok );
if ( ok ) {
qreal h = r.height();
QRectF cr( r.x(), r.y()+h/4.,
r.width()*completion/100., h/2.+1 /*??*/ );
QColor compcolor( painter->pen().color() );
compcolor.setAlpha( 150 );
painter->fillRect( cr, compcolor );
}
painter->restore();
}
break;
case TypeSummary:
if ( opt.itemRect.isValid() ) {
// TODO
pw-=1;
const QRectF r = QRectF( opt.itemRect ).adjusted( -pw, -pw, pw, pw );
QPainterPath path;
const qreal deltaY = r.height()/2.;
const qreal deltaXBezierControl = .25*qMin( r.width(), r.height() );
const qreal deltaX = qMin( r.width()/2., r.height() );
path.moveTo( r.topLeft() );
path.lineTo( r.topRight() );
path.lineTo( QPointF( r.right(), r.top() + 2.*deltaY ) );
//path.lineTo( QPointF( r.right()-3./2.*delta, r.top() + delta ) );
path.quadTo( QPointF( r.right()-deltaXBezierControl, r.top() + deltaY ), QPointF( r.right()-deltaX, r.top() + deltaY ) );
//path.lineTo( QPointF( r.left()+3./2.*delta, r.top() + delta ) );
path.lineTo( QPointF( r.left() + deltaX, r.top() + deltaY ) );
path.quadTo( QPointF( r.left()+deltaXBezierControl, r.top() + deltaY ), QPointF( r.left(), r.top() + 2.*deltaY ) );
path.closeSubpath();
painter->setBrushOrigin( itemRect.topLeft() );
painter->save();
painter->translate( 0.5, 0.5 );
painter->drawPath( path );
painter->restore();
}
break;
case TypeEvent: /* TODO */
//qDebug() << opt.boundingRect << opt.itemRect;
if ( opt.boundingRect.isValid() ) {
const qreal pw = painter->pen().width() / 2. - 1;
const QRectF r = QRectF( opt.itemRect ).adjusted( -pw, -pw, pw, pw ).translated( -opt.itemRect.height()/2, 0 );
QPainterPath path;
const qreal delta = static_cast< int >( r.height() / 2 );
path.moveTo( delta, 0. );
path.lineTo( 2.*delta, delta );
path.lineTo( delta, 2.*delta );
path.lineTo( 0., delta );
path.closeSubpath();
painter->save();
painter->translate( r.topLeft() );
painter->translate( 0, 0.5 );
painter->drawPath( path );
painter->restore();
#if 0
painter->setBrush( Qt::NoBrush );
painter->setPen( Qt::black );
painter->drawRect( opt.boundingRect );
painter->setPen( Qt::red );
painter->drawRect( r );
#endif
}
break;
default:
drawText = false;
break;
}
Qt::Alignment ta;
switch ( opt.displayPosition ) {
case StyleOptionGanttItem::Left: ta = Qt::AlignLeft; break;
case StyleOptionGanttItem::Right: ta = Qt::AlignRight; break;
case StyleOptionGanttItem::Center: ta = Qt::AlignCenter; break;
case StyleOptionGanttItem::Hidden: drawText = false; break;
}
if ( drawText ) {
painter->drawText( boundingRect, ta | Qt::AlignVCenter, txt );
}
painter->restore();
}
(2).拖动事件:
在文件kdganttgraphicsview.h中添加信号
void signal_dataChanged( const QModelIndex & index );
void GraphicsView::Private::slotDataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight )
{
const QModelIndex parent = topLeft.parent();
for ( int row = topLeft.row(); row <= bottomRight.row(); ++row ) {
scene.updateRow( scene.summaryHandlingModel()->index( row, 0, parent ) );
}
emit q->signal_dataChanged(topLeft);
}
外部使用:
connect(ui->ganttView->graphicsView(), SIGNAL(signal_dataChanged(const QModelIndex&)), this, SLOT(onCheckTask(const QModelIndex&)));
修改显示的日期格式,效果:
上面显示年月,下面显示多少号,显示号很简单:
grid.setUserDefinedLowerScale(new KDGantt::DateTimeScaleFormatter(KDGantt::DateTimeScaleFormatter::Day,QString::fromLatin1("dd"),QString::fromLatin1("%1"),Qt::AlignHCenter));
其中有个格式:QString::fromLatin1("dd"),dd:号, ddd:星期几
使用这个函数,只能显示年或者月或者天,不能组合显示,所有重写DateTimeScaleFormatter;
class MyDateTimeScaleFormatter : public KDGantt::DateTimeScaleFormatter
{
public:
MyDateTimeScaleFormatter() : DateTimeScaleFormatter(Month, "MM"){}
/*reimp*/QDateTime nextRangeBegin(const QDateTime& datetime) const
{
return currentRangeBegin(datetime).addMonths(1);
}
/*reimp*/QDateTime currentRangeBegin(const QDateTime& datetime) const
{
return datetime;
}
/*reimp*/QString text(const QDateTime& dt) const
{
return QObject::tr("%1年%2月").arg(dt.date().year()).arg(dt.date().month());
}
};
grid.setUserDefinedUpperScale(new MyDateTimeScaleFormatter()); // 显示在上面