代码存档:QPainterPath虚拟打印机
功能:在界面依次打印QPainterPath中的元素。代码作者:Qwen3-Coder大模型。
·
功能:在界面依次打印QPainterPath中的元素。
代码作者:Qwen3-Coder大模型。
#ifndef VIRTUALPRINTERWIDGET_H
#define VIRTUALPRINTERWIDGET_H
#include <QWidget>
#include <QPainterPath>
#include <QTimer>
class VirtualPrinterWidget : public QWidget {
Q_OBJECT
public:
explicit VirtualPrinterWidget(QWidget *parent = nullptr);
void setPath(const QPainterPath &path);
void startPrinting();
void stopPrinting();
void resetPrinter();
bool isPrintingActive() const;
protected:
void paintEvent(QPaintEvent *event) override;
private slots:
void onTimerTimeout();
private:
QPointF cubicBezierPoint(const QPointF &p0, const QPointF &c1, const QPointF &c2, const QPointF &p1, qreal t);
void updateCurrentPartialElementPath();
QPainterPath fullPath;
QPainterPath partialPath;
QPainterPath currentPartialElementPath;
QTimer *timer;
int currentPathIndex;
qreal currentSubPathProgress;
bool isPrinting;
};
#endif // VIRTUALPRINTERWIDGET_H
#include "VirtualPrinterWidget.h"
#include <QPainter>
#include <QTimer>
#include <QDebug>
#include <QPen>
#include <QFont>
VirtualPrinterWidget::VirtualPrinterWidget(QWidget *parent)
: QWidget(parent) {
currentPathIndex = 0;
currentSubPathProgress = 0.0;
isPrinting = false;
timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &VirtualPrinterWidget::onTimerTimeout);
setWindowTitle("Virtual Printer");
resize(800, 600);
}
void VirtualPrinterWidget::setPath(const QPainterPath &path) {
fullPath = path;
resetPrinter();
update();
}
void VirtualPrinterWidget::startPrinting() {
if (isPrinting) return;
isPrinting = true;
timer->start(10);
}
void VirtualPrinterWidget::stopPrinting() {
timer->stop();
isPrinting = false;
}
void VirtualPrinterWidget::resetPrinter()
{
stopPrinting();
currentPathIndex = 0;
currentSubPathProgress = 0.0;
partialPath = QPainterPath();
currentPartialElementPath = QPainterPath(); // 重置当前元素路径
}
bool VirtualPrinterWidget::isPrintingActive() const {
return isPrinting;
}
void VirtualPrinterWidget::paintEvent(QPaintEvent *event) {
Q_UNUSED(event);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.fillRect(rect(), Qt::white);
// 绘制已完成的部分 (蓝色)
QPen pen(Qt::blue, 2);
painter.setPen(pen);
painter.drawPath(partialPath);
// 绘制当前正在绘制的部分 (红色)
// 只有当 progress < 1.0 时才绘制,避免在完成瞬间重复绘制
if (currentPathIndex < fullPath.elementCount() && isPrinting && currentSubPathProgress < 1.0) {
QPen currentPen(Qt::red, 2);
painter.setPen(currentPen);
painter.drawPath(currentPartialElementPath);
}
// 绘制状态信息
painter.setPen(Qt::black);
painter.drawText(10, height() - 10, QString("Element: %1/%2, Progress: %3").arg(currentPathIndex).arg(fullPath.elementCount()).arg(currentSubPathProgress, 0, 'f', 2));
}
void VirtualPrinterWidget::onTimerTimeout() {
if (!isPrinting) {
timer->stop();
return;
}
if (currentPathIndex >= fullPath.elementCount()) {
stopPrinting();
return;
}
// 计算当前元素的进度
currentSubPathProgress += 0.05;
if (currentSubPathProgress >= 1.0) {
currentSubPathProgress = 1.0; // 确保精确为1.0
}
// 更新当前元素的绘制路径
updateCurrentPartialElementPath();
if (currentSubPathProgress >= 1.0) {
// 进度完成,将当前元素的完整路径合并到已完成部分
// 此时 currentPartialElementPath 包含完整元素
partialPath.addPath(currentPartialElementPath);
// 重置当前元素路径
currentPartialElementPath = QPainterPath();
// 移动到下一个元素
auto element = fullPath.elementAt(currentPathIndex);
if (element.type == QPainterPath::MoveToElement) {
currentPathIndex++;
} else if (element.type == QPainterPath::LineToElement) {
currentPathIndex++;
} else if (element.type == QPainterPath::CurveToElement) {
// 贝塞尔曲线需要跳过两个点 (C2, P1)
if (currentPathIndex + 2 < fullPath.elementCount()) {
currentPathIndex += 3; // 跳过 CurveTo, CurveTo, LineTo (或MoveTo)
} else {
// 格式错误,跳过当前元素并警告
qDebug() << "Warning: Malformed curve, skipping.";
currentPathIndex++;
}
}
// 重置进度,准备绘制下一个元素
currentSubPathProgress = 0.0;
}
update(); // 请求重绘
}
void VirtualPrinterWidget::updateCurrentPartialElementPath() {
// 清空当前元素路径,重新计算
currentPartialElementPath = QPainterPath();
if (currentPathIndex >= fullPath.elementCount()) {
return;
}
auto element = fullPath.elementAt(currentPathIndex);
if (element.type == QPainterPath::MoveToElement) {
// MoveTo 本身不绘制,但它定义了下一个 LineTo 或 CurveTo 的起点
// 如果下一个元素是 LineTo 或 CurveTo,则开始绘制那部分
if (currentPathIndex + 1 < fullPath.elementCount()) {
auto nextElement = fullPath.elementAt(currentPathIndex + 1);
if (nextElement.type == QPainterPath::LineToElement) {
// 绘制从 MoveTo 到 LineTo 的部分
QPointF p1 = QPointF(element.x, element.y);
QPointF p2 = QPointF(nextElement.x, nextElement.y);
QPointF currentPos = p1 + (p2 - p1) * currentSubPathProgress;
currentPartialElementPath.moveTo(p1);
currentPartialElementPath.lineTo(currentPos);
} else if (nextElement.type == QPainterPath::CurveToElement && currentPathIndex + 2 < fullPath.elementCount()) {
// 处理 MoveTo -> CurveTo -> CurveTo -> (LineTo/MoveTo)
auto ctrlPt1 = QPointF(nextElement.x, nextElement.y);
auto ctrlPt2 = QPointF(fullPath.elementAt(currentPathIndex + 2).x, fullPath.elementAt(currentPathIndex + 2).y);
auto endPt = QPointF(fullPath.elementAt(currentPathIndex + 3).x, fullPath.elementAt(currentPathIndex + 3).y);
QPointF p1 = QPointF(element.x, element.y);
QPointF currentPos = cubicBezierPoint(p1, ctrlPt1, ctrlPt2, endPt, currentSubPathProgress);
currentPartialElementPath.moveTo(p1);
currentPartialElementPath.lineTo(currentPos); // 简化显示,用直线段模拟
}
}
} else if (element.type == QPainterPath::LineToElement) {
// 找到上一个点 (MoveTo 或 LineTo 或 CurveTo的终点)
// 简化:假设起点是 partialPath 的最后一个点或全局起点
QPointF startPoint;
if (partialPath.elementCount() > 0) {
auto lastElement = partialPath.elementAt(partialPath.elementCount() - 1);
startPoint = QPointF(lastElement.x, lastElement.y);
} else {
// 如果 partialPath 为空,需要找到当前 LineTo 的逻辑起点
// 通常在 LineTo 之前会有一个 MoveTo
int prevIndex = currentPathIndex - 1;
while (prevIndex >= 0) {
if (fullPath.elementAt(prevIndex).type == QPainterPath::MoveToElement) {
startPoint = QPointF(fullPath.elementAt(prevIndex).x, fullPath.elementAt(prevIndex).y);
break;
}
prevIndex--;
}
// 如果找不到 MoveTo,则起点可能是 (0,0) 或上一个 LineTo,这里简化处理
}
QPointF p1 = startPoint;
QPointF p2 = QPointF(element.x, element.y);
QPointF currentPos = p1 + (p2 - p1) * currentSubPathProgress;
currentPartialElementPath.moveTo(p1);
currentPartialElementPath.lineTo(currentPos);
} else if (element.type == QPainterPath::CurveToElement) {
// 处理三次贝塞尔曲线 (MoveTo P0, CurveTo C1, CurveTo C2, LineTo/MoveTo P1)
int curveStartIndex = currentPathIndex - 1; // 理论上是 MoveTo
if (curveStartIndex >= 0 && fullPath.elementAt(curveStartIndex).type == QPainterPath::MoveToElement &&
currentPathIndex + 2 < fullPath.elementCount() &&
fullPath.elementAt(currentPathIndex + 1).type == QPainterPath::CurveToElement && // C2
(fullPath.elementAt(currentPathIndex + 2).type == QPainterPath::LineToElement || fullPath.elementAt(currentPathIndex + 2).type == QPainterPath::MoveToElement) // P1
) {
QPointF p0(fullPath.elementAt(curveStartIndex).x, fullPath.elementAt(curveStartIndex).y); // 起点
QPointF c1(fullPath.elementAt(currentPathIndex).x, fullPath.elementAt(currentPathIndex).y); // 控制点1
QPointF c2(fullPath.elementAt(currentPathIndex + 1).x, fullPath.elementAt(currentPathIndex + 1).y); // 控制点2
QPointF p1(fullPath.elementAt(currentPathIndex + 2).x, fullPath.elementAt(currentPathIndex + 2).y); // 终点
QPointF currentPos = cubicBezierPoint(p0, c1, c2, p1, currentSubPathProgress);
currentPartialElementPath.moveTo(p0);
currentPartialElementPath.lineTo(currentPos); // 简化显示
} else {
qDebug() << "Warning: Malformed curve in path during update, skipping.";
// 如果格式不匹配,currentPartialElementPath 保持为空
}
}
}
QPointF VirtualPrinterWidget::cubicBezierPoint(const QPointF &p0, const QPointF &c1, const QPointF &c2, const QPointF &p1, qreal t) {
qreal u = 1 - t;
qreal tt = t * t;
qreal uu = u * u;
qreal uuu = uu * u;
qreal ttt = tt * t;
QPointF p = uuu * p0;
p += 3 * uu * t * c1;
p += 3 * u * tt * c2;
p += ttt * p1;
return p;
}
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)