这是一个垂直的时间轴,显示效果非常简单,但不能显示有格式的文本。如果想显示有格式文本可以把右侧显示本文的位置换成QLabel控件去显示有格式文本。
如果想改变文本行间距,根据我目前在网上搜索的答案,只能用QLabel配合qss样式表(即控件的styleSheet属性)实现。根据此控件可以学习Qt多行文本尺寸的测量和绘制方法。此控件使用方法是new MTimeLine,然后用append函数添加项,或removeAt函数删除项就可以了。
此代码在VS2015和Qt5.9上测试通过。
下面是效果图:
上代码,头文件:
class MTimeLinePrivate : public QWidget
{
Q_OBJECT
public:
MTimeLinePrivate(QWidget* parent = 0);
void append(const QDate& date, const QString& str);
void removeAt(int index);
private:
void paintEvent(QPaintEvent *event) override;
void resizeEvent(QResizeEvent*) override;
void showEvent(QShowEvent*) override;
QSize calcContentSize();
QSize calcDateStringSize(const QFontMetrics& fm) const;
void paintDateBkShape(QPainter* painter, const QRect& rect, int whereisy);
void paintTextBkShape(QPainter* painter, const QRect& rect, int whereisy);
struct EventInfo
{
QDate date;
QString content;
};
private:
static const QMargins contentm; /* 控件内容的边距 */
static const QMargins datep; /* 日期文本的边距 */
static const QMargins datem; /* 日期文本背景矩形的外边距 */
static const QMargins textp; /* 事件文本的边距 */
static const QMargins textm; /* 事件文本背景矩形的外边距 */
QVector<EventInfo> events;
};
class MTimeLine : public QWidget
{
Q_OBJECT
public:
MTimeLine(QWidget* parent = 0);
void append(const QDate& date, const QString& str);
void removeAt(int index);
private:
MTimeLinePrivate* tlReal;
};
CPP文件:
const QMargins MTimeLinePrivate::contentm(10, 6, 10, 6);
const QMargins MTimeLinePrivate::datep(10, 10, 10, 10);
const QMargins MTimeLinePrivate::datem(0, 6, 14, 6);
const QMargins MTimeLinePrivate::textp(12, 12, 12, 12);
const QMargins MTimeLinePrivate::textm(14, 6, 0, 6);
MTimeLinePrivate::MTimeLinePrivate(QWidget* parent) :
QWidget(parent)
{
}
void MTimeLinePrivate::append(const QDate& date, const QString& str)
{
events.push_back({ date, str });
if (isVisible())
{
QSize mini = calcContentSize();
setMinimumSize(mini);
update();
}
}
void MTimeLinePrivate::removeAt(int index)
{
events.remove(index);
if (isVisible())
{
QSize mini = calcContentSize();
setMinimumSize(mini);
update();
}
}
void MTimeLinePrivate::showEvent(QShowEvent*)
{
QSize mini = calcContentSize();
setMinimumSize(mini);
}
void MTimeLinePrivate::resizeEvent(QResizeEvent*)
{
QSize mini = calcContentSize();
setMinimumSize(mini);
}
QSize MTimeLinePrivate::calcContentSize()
{
QFontMetrics fm = fontMetrics();
QSize datesz = calcDateStringSize(fm);
int ally = contentm.top();
int datex = contentm.left();
int midx = contentm.left() + datem.left() + datep.left() + datesz.width() + datep.right() + datem.right();
int textx = midx;
for (const auto &item : events)
{
QRect dateOuterRect;
dateOuterRect.setX(datex + datem.left());
dateOuterRect.setY(ally + datem.top());
dateOuterRect.setWidth(datep.left() + datesz.width() + datep.right());
dateOuterRect.setHeight(datep.top() + datesz.height() + datep.bottom());
QRect dateRect;
dateRect.setX(dateOuterRect.x() + datep.left());
dateRect.setY(dateOuterRect.y() + datep.top());
dateRect.setSize(datesz);
int charx = textx + textp.left() + textm.left();
int chary = ally + textm.top() + textp.top();
int charw = width() - charx - (textp.right() + textm.right() + contentm.right());
QRect textRect = fm.boundingRect(charx, chary, charw, 12000, Qt::TextWordWrap, item.content);
QRect textOuterRect;
textOuterRect.setX(textx + textm.left());
textOuterRect.setY(ally + textm.top());
textOuterRect.setWidth(textp.left() + textRect.width() + textp.right());
textOuterRect.setHeight(textp.top() + textRect.height() + textp.bottom());
int midh1 = datem.top() + dateOuterRect.height() + datem.bottom(); /* 左侧高度 */
int midh2 = textm.top() + textOuterRect.height() + textm.bottom(); /* 右侧高度 */
int midy2 = ally + qMax(midh1, midh2);
ally = midy2;
}
return QSize(2 * midx, ally + contentm.bottom());
}
void MTimeLinePrivate::paintDateBkShape(QPainter* painter, const QRect& rect, int whereisy)
{
const QPoint triangle[] =
{
{ 0, -6 },
{ 7, 0 },
{ 0, 6 },
};
painter->setPen(Qt::NoPen);
painter->setBrush(QColor(0x34, 0x98, 0xdb));
painter->drawRoundedRect(rect, 6, 6);
painter->save();
painter->translate(rect.right(), whereisy);
painter->drawPolygon(triangle, 3);
painter->restore();
}
void MTimeLinePrivate::paintTextBkShape(QPainter* painter, const QRect& rect, int whereisy)
{
const QPoint triangle[] =
{
{ 0, -6 },
{ -7, 0 },
{ 0, 6 },
};
painter->setPen(Qt::NoPen);
painter->setBrush(QColor(0xec, 0xf0, 0xf1));
painter->drawRoundedRect(rect, 6, 6);
painter->save();
painter->translate(rect.x(), whereisy);
painter->drawPolygon(triangle, 3);
painter->restore();
}
QSize MTimeLinePrivate::calcDateStringSize(const QFontMetrics& fm) const
{
QDate today = QDate::currentDate();
QSize datesz = fm.size(0, today.toString(u8"yyyy年MM月dd日"));
return datesz;
}
void MTimeLinePrivate::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.fillRect(0, 0, width(), height(), Qt::white); /* 白色背景 */
QFontMetrics fm = painter.fontMetrics();
QSize datesz = calcDateStringSize(fm);
int ally = contentm.top();
int datex = contentm.left();
int midx = contentm.left() + datem.left() + datep.left() + datesz.width() + datep.right() + datem.right();
int textx = midx;
bool odd = true; /* 奇偶数,用来区分绘制点的样式 */
for (const auto &item : events)
{
/* 绘制左侧日期 */
QRect dateOuterRect;
dateOuterRect.setX(datex + datem.left());
dateOuterRect.setY(ally + datem.top());
dateOuterRect.setWidth(datep.left() + datesz.width() + datep.right());
dateOuterRect.setHeight(datep.top() + datesz.height() + datep.bottom());
const int doty = dateOuterRect.center().y();
paintDateBkShape(&painter, dateOuterRect, doty);
QRect dateRect;
dateRect.setX(dateOuterRect.x() + datep.left());
dateRect.setY(dateOuterRect.y() + datep.top());
dateRect.setSize(datesz);
painter.setPen(Qt::black);
painter.drawText(dateRect, 0, item.date.toString(u8"yyyy年MM月dd日"));
/* 绘制右侧字符串 */
int charx = textx + textp.left() + textm.left();
int chary = ally + textm.top() + textp.top();
int charw = width() - charx - (textp.right() + textm.right() + contentm.right());
QRect textRect = fm.boundingRect(charx, chary, charw, 12000, Qt::TextWordWrap, item.content);
QRect textOuterRect;
textOuterRect.setX(textx + textm.left());
textOuterRect.setY(ally + textm.top());
textOuterRect.setWidth(textp.left() + textRect.width() + textp.right());
textOuterRect.setHeight(textp.top() + textRect.height() + textp.bottom());
paintTextBkShape(&painter, textOuterRect, doty);
painter.setPen(Qt::black);
painter.drawText(textRect, Qt::TextWordWrap, item.content);
/* 绘制中间的线条和点 */
int midh1 = datem.top() + dateOuterRect.height() + datem.bottom(); /* 左侧高度 */
int midh2 = textm.top() + textOuterRect.height() + textm.bottom(); /* 右侧高度 */
int midy2 = ally + qMax(midh1, midh2);
painter.setPen(QPen(QColor(0, 170, 255), 2));
painter.drawLine(QPoint(midx, ally), QPoint(midx, midy2));
painter.setPen(QPen(QColor(0, 170, 255), 8, Qt::SolidLine, odd ? Qt::SquareCap : Qt::RoundCap));
painter.drawPoint(midx, doty);
odd = !odd;
ally = midy2;
}
}
/
MTimeLine::MTimeLine(QWidget* parent) :
QWidget(parent)
{
QGridLayout* lay = new QGridLayout(this);
lay->setContentsMargins(0, 0, 0, 0);
QScrollArea* area = new QScrollArea(this);
tlReal = new MTimeLinePrivate(this);
area->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
area->setWidget(tlReal);
area->setWidgetResizable(true);
lay->addWidget(area);
setLayout(lay);
}
void MTimeLine::append(const QDate& date, const QString& str)
{
tlReal->append(date, str);
}
void MTimeLine::removeAt(int index)
{
tlReal->removeAt(index);
}
本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(Qt实战项目,C++语言基础,C++设计模式,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓