最近不忙,所以自己用QWidget 绘制一个图表。
主要功能:
1. 一次可添加多条线,每条颜色都不一样
2. 线条做了平滑处理(QPainterPath::cubicTo)
3. 点击线条后,线条加粗并显示每个数据坐标
4. 点击线条上的小点, 可以用 鼠标、键盘 移动位置,在右侧会显示点的坐标值
5. 修改右侧坐标值,线条中的点也会随之移动
6. 鼠标右键可以删除线条
7. 点击顶部颜色框会隐藏对应的线条
8. 鼠标右键点击顶部颜色框,可以删除对应线条
9. 将QWidget 存为图片
几点总结:
1. 用绘制路径(QPainterPath)绘制多条线条时, 他们的内部会填充颜色,解决方法是将填充变成无: painter.setBrush(Qt::NoBrush); 同理, 绘制图形不要边框也可以将pen 设置为 : painter.setPen(Qt::NoPen);
2. 线条平滑处理
// 转换坐标值
QList<QPointF> drawPoint = conversionCoordinates(eachLine.vecPoint);
// 计算绘制路径,做平滑处理(方法二)
// 3次贝塞尔曲线
drawPath.clear();
drawPath.moveTo(drawPoint[0]);
for (int i = 0; i < eachLine.drawPoint.count() - 1; i++)
{
QPointF sp = drawPoint[i];
QPointF ep = drawPoint[i + 1];
QPointF c1 = QPointF((sp.x() + ep.x()) * 0.5, sp.y());
QPointF c2 = QPointF((sp.x() + ep.x()) * 0.5, ep.y());
drawPath.cubicTo(c1,c2,ep);
}
// 路径绘制
painter.drawPath(drawPath);
3. 条线点击选择判断
用 QPainterPathStroker 将路径加宽,增大选择区域, 通过 contains 判断点是否在路径中。
// 特别要注意笔宽度(其他图形类似于边框宽度)
QPainterPathStroker painterPathStroker;
// 加大路径选择面积
painterPathStroker.setWidth(15);
// 生成加宽后的路径
QPainterPath newPath = painterPathStroker.createStroke(drawPath);
if(newPath.contains(event->pos()))
{
// 线条被点击
}
4. 判断线条那个点被点击
先查找数据中,那个两个数据的X值与点击点的x值接近, 然后用两点距离公式计算距离,判断哪个点被点击。(我自己写了个二分法查找,因为我的数据是QPointF,没法用现成函数)
int MyDrawWidget::my_lower_bound(QList<QPointF> &drawPoint, qreal mouse_x)
{
int count = drawPoint.length();
int step = 0;
int index = 0;
while (count>0) {
step = count / 2;
if (drawPoint[step + index].x() < mouse_x)
{
count -= step + 1;
index += step + 1;
}
else
{
count=step;
}
}
qDebug() << index;
return index;
}
5. 图表四边最好用四个常量,写起来麻烦,最后调整方便
6. 顶部的颜色块是 (QPushButton)
我重写了这个控件,用来控制背景颜色和大小, 只用到了 paintEvent 和 mousePressEvent, 点击事件自己写了个信号,在 mousePressEvent触发。
7. QWidget 保存图片
QString fileName = QFileDialog::getSaveFileName(this,"保存图片","C:\\Users\\Administrator\\Desktop\\程序测试图片.jpg","*.jpg");
QPixmap map = QPixmap::grabWidget(ui->my_DrawWidget);
map.save(fileName,"jpg");
}
结语:
写完这个后,发现自己对 绘制、选择、控件自定义、信号槽、代码架构设计等有了更多理解。眼过千遍不如手过一遍,代码是敲出来的,自己的路是自己走出来的!!!