Skip to content

Commit 5c58e0e

Browse files
committed
[图形框架优化]: 添加帧率限制器并重构图形项交互逻辑
-**新增帧率限制器**: 添加FrameRateLimiter类,通过事件过滤器控制绘制频率,支持设置目标FPS -**重构图形项基类**: 统一hover预览和边缘检测逻辑,新增updateHoverPreview和detectEdgeRegion虚函数 -**优化圆弧项交互**: 移除临时路径计算,改进边缘区域检测和光标反馈 -**改进圆形项**: 简化悬停预览逻辑,增强边缘区域识别精度 -**重构线形项**: 整合预览绘制逻辑,移除临时线条存储 -**优化多边形项**: 统一预览绘制机制,改进边界检查 -**完善圆环项**: 重构边缘检测逻辑,提升交互体验 -**改进旋转矩形**: 重构角点移动逻辑,增强旋转控制线交互 -**优化圆角矩形**: 简化预览绘制,改进边缘检测算法 -**增强工具函数**: 添加isPointNearEdge函数,支持精确的边缘距离检测 -**调整视图初始化**: 提取initializeView方法,统一视图设置逻辑
1 parent 3703172 commit 5c58e0e

25 files changed

Lines changed: 502 additions & 416 deletions

examples/graphics/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ set(PROJECT_SOURCES
1515
drawscene.hpp
1616
drawwidget.cpp
1717
drawwidget.h
18+
frameratelimiter.cc
19+
frameratelimiter.hpp
1820
icoconverterwidget.cc
1921
icoconverterwidget.hpp
2022
imagecaptureview.cc
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#include "frameratelimiter.hpp"
2+
3+
#include <QDebug>
4+
#include <QElapsedTimer>
5+
#include <QEvent>
6+
#include <QWidget>
7+
8+
class FrameRateLimiter::FrameRateLimiterPrivate
9+
{
10+
public:
11+
explicit FrameRateLimiterPrivate(FrameRateLimiter *q)
12+
: q_ptr(q)
13+
{}
14+
15+
FrameRateLimiter *q_ptr;
16+
17+
qint64 targetFrameTime = 1000 / 60; // 毫秒
18+
QElapsedTimer frameTimer;
19+
};
20+
21+
FrameRateLimiter::FrameRateLimiter(QObject *parent)
22+
: QObject{parent}
23+
, d_ptr(new FrameRateLimiterPrivate(this))
24+
{}
25+
26+
FrameRateLimiter::~FrameRateLimiter() {}
27+
28+
void FrameRateLimiter::setTargetFPS(int fps)
29+
{
30+
d_ptr->targetFrameTime = 1000 / qMax(1, fps);
31+
}
32+
33+
bool FrameRateLimiter::eventFilter(QObject *watched, QEvent *event)
34+
{
35+
qDebug() << "FrameRateLimiter::eventFilter:" << event->type();
36+
switch (event->type()) {
37+
case QEvent::Paint:
38+
if (!d_ptr->frameTimer.isValid()) {
39+
d_ptr->frameTimer.start();
40+
} else if (!d_ptr->frameTimer.hasExpired(d_ptr->targetFrameTime)) {
41+
qDebug() << "Has not expired";
42+
auto *widget = qobject_cast<QWidget *>(watched);
43+
if (widget) {
44+
QMetaObject::invokeMethod(
45+
widget, [widget] { widget->update(); }, Qt::QueuedConnection);
46+
}
47+
return true;
48+
} else {
49+
d_ptr->frameTimer.restart();
50+
}
51+
break;
52+
default: break;
53+
}
54+
55+
return QObject::eventFilter(watched, event);
56+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#pragma once
2+
3+
#include <QObject>
4+
5+
class FrameRateLimiter : public QObject
6+
{
7+
Q_OBJECT
8+
public:
9+
explicit FrameRateLimiter(QObject *parent = nullptr);
10+
~FrameRateLimiter() override;
11+
12+
void setTargetFPS(int fps);
13+
14+
protected:
15+
bool eventFilter(QObject *watched, QEvent *event) override;
16+
17+
private:
18+
class FrameRateLimiterPrivate;
19+
QScopedPointer<FrameRateLimiterPrivate> d_ptr;
20+
};

examples/graphics/graphics.pro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ SOURCES += \
2626
customlineitem.cc \
2727
drawscene.cc \
2828
drawwidget.cpp \
29+
frameratelimiter.cc \
2930
icoconverterwidget.cc \
3031
imagecaptureview.cc \
3132
imageviewer.cpp \
@@ -53,6 +54,7 @@ HEADERS += \
5354
customlineitem.hpp \
5455
drawscene.hpp \
5556
drawwidget.h \
57+
frameratelimiter.hpp \
5658
icoconverterwidget.hpp \
5759
imagecaptureview.hpp \
5860
imageviewer.h \

src/graphics/graphicsarcitem.cpp

Lines changed: 57 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ void GraphicsArcItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
165165

166166
switch (mouseRegion()) {
167167
case GraphicsBasicItem::MouseRegion::All: pts_tmp.translate(dp); break;
168-
case GraphicsBasicItem::MouseRegion::None: {
168+
case GraphicsBasicItem::MouseRegion::Edge: {
169169
switch (d_ptr->mouseRegion) {
170170
case InEdge0:
171171
setMyCursor(d_ptr->arch.center, event->scenePos());
@@ -206,71 +206,22 @@ void GraphicsArcItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
206206
pointsChanged(pts_tmp);
207207
}
208208

209-
void GraphicsArcItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
210-
{
211-
auto pts_tmp = geometryCache()->controlPoints();
212-
QPointF point = event->scenePos();
213-
if (pts_tmp.size() == 2 || pts_tmp.size() == 3) {
214-
pts_tmp.append(point);
215-
showHoverArc(pts_tmp);
216-
}
217-
if (!isValid()) {
218-
return;
219-
}
220-
GraphicsBasicItem::hoverMoveEvent(event);
221-
if (mouseRegion() == GraphicsBasicItem::MouseRegion::DotRegion) {
222-
return;
223-
}
224-
setMouseRegion(GraphicsBasicItem::MouseRegion::None);
225-
226-
QPointF p1 = Utils::pointFromCenter(d_ptr->arch.center,
227-
d_ptr->arch.maxRadius,
228-
d_ptr->arch.startAngle);
229-
QPointF p2 = Utils::pointFromCenter(d_ptr->arch.center,
230-
d_ptr->arch.maxRadius,
231-
d_ptr->arch.endAngle);
232-
QLineF line1(p1, pts_tmp.at(0));
233-
QLineF line2(p2, pts_tmp.at(1));
234-
if (qAbs(Utils::distance(point, d_ptr->arch.center) - d_ptr->arch.minRadius) < margin() / 3) {
235-
d_ptr->mouseRegion = InEdge0;
236-
setMyCursor(d_ptr->arch.center, point);
237-
} else if (qAbs(Utils::distance(point, d_ptr->arch.center) - d_ptr->arch.maxRadius)
238-
< margin() / 3) {
239-
d_ptr->mouseRegion = InEdge1;
240-
setMyCursor(d_ptr->arch.center, point);
241-
} else if (Utils::boundingFromLine(line1, margin() / 4).containsPoint(point, Qt::OddEvenFill)) {
242-
d_ptr->mouseRegion = InEdgeL;
243-
setCursor(Utils::cursorForDirection(line1.angle()));
244-
} else if (Utils::boundingFromLine(line2, margin() / 4).containsPoint(point, Qt::OddEvenFill)) {
245-
d_ptr->mouseRegion = InEdgeH;
246-
setCursor(Utils::cursorForDirection(line2.angle()));
247-
} else if (d_ptr->arcPath.contains(point)) {
248-
setMouseRegion(GraphicsBasicItem::MouseRegion::All);
249-
setCursor(Qt::SizeAllCursor);
250-
} else {
251-
unsetCursor();
252-
}
253-
}
254-
255209
void GraphicsArcItem::drawContent(QPainter *painter)
256210
{
257211
painter->drawPath(isValid() ? d_ptr->arcPath : d_ptr->cachePath);
258212
}
259213

260214
void GraphicsArcItem::pointsChanged(const QPolygonF &ply)
261215
{
262-
auto rect = scene()->sceneRect();
263-
if (!rect.contains(ply.last())) {
264-
return;
265-
}
266-
267216
if (ply.size() == 3) {
268217
double deltaAngle = QLineF(ply[0], ply[1]).angleTo(QLineF(ply[0], ply[2]));
269218
if (deltaAngle < 0.00001 || deltaAngle > 355.99999) {
270219
return;
271220
}
272221
}
273222

223+
auto sceneRect = scene()->sceneRect();
224+
274225
switch (ply.size()) {
275226
case 1:
276227
case 2: geometryCache()->setControlPoints(ply); break;
@@ -279,7 +230,7 @@ void GraphicsArcItem::pointsChanged(const QPolygonF &ply)
279230
return;
280231
}
281232
QPolygonF polygon = d_ptr->cachePath.toFillPolygon() + ply;
282-
if (!rect.contains(polygon.boundingRect())) {
233+
if (!sceneRect.contains(polygon.boundingRect())) {
283234
return;
284235
}
285236
geometryCache()->setControlPoints(ply);
@@ -295,21 +246,69 @@ void GraphicsArcItem::pointsChanged(const QPolygonF &ply)
295246
update();
296247
}
297248

298-
void GraphicsArcItem::showHoverArc(const QPolygonF &ply)
249+
void GraphicsArcItem::updateHoverPreview(const QPointF &scenePos)
299250
{
300-
switch (ply.size()) {
251+
auto controlPoints = geometryCache()->controlPoints();
252+
auto size = controlPoints.size();
253+
if (size < 2 || size > 3) {
254+
return;
255+
}
256+
257+
controlPoints.append(scenePos);
258+
size = controlPoints.size();
259+
260+
switch (size) {
301261
case 3:
302262
// QPainterPath::arcTo: Adding point with invalid coordinates, ignoring call
303-
if (Utils::distance(ply[1], ply[2]) < margin()) {
263+
if (Utils::distance(controlPoints[1], controlPoints[2]) < margin()) {
304264
return;
305265
}
306-
Utils::calculateHalfArc(ply, d_ptr->cachePath);
266+
Utils::calculateHalfArc(controlPoints, d_ptr->cachePath);
307267
break;
308-
case 4: Utils::calculateAllArc(ply, d_ptr->cachePath, margin()); break;
268+
case 4: Utils::calculateAllArc(controlPoints, d_ptr->cachePath, margin()); break;
309269
default: return;
310270
}
311271

312272
update();
313273
}
314274

275+
GraphicsBasicItem::MouseRegion GraphicsArcItem::detectEdgeRegion(const QPointF &scenePos)
276+
{
277+
const double marginValue = margin() / 2.0;
278+
auto controlPoints = geometryCache()->controlPoints();
279+
auto p1 = Utils::pointFromCenter(d_ptr->arch.center,
280+
d_ptr->arch.maxRadius,
281+
d_ptr->arch.startAngle);
282+
auto p2 = Utils::pointFromCenter(d_ptr->arch.center,
283+
d_ptr->arch.maxRadius,
284+
d_ptr->arch.endAngle);
285+
QLineF line1(p1, controlPoints[0]);
286+
QLineF line2(p2, controlPoints[1]);
287+
288+
if (qAbs(Utils::distance(scenePos, d_ptr->arch.center) - d_ptr->arch.minRadius) < marginValue) {
289+
d_ptr->mouseRegion = InEdge0;
290+
setMyCursor(d_ptr->arch.center, scenePos);
291+
} else if (qAbs(Utils::distance(scenePos, d_ptr->arch.center) - d_ptr->arch.maxRadius)
292+
< marginValue) {
293+
d_ptr->mouseRegion = InEdge1;
294+
setMyCursor(d_ptr->arch.center, scenePos);
295+
} else if (Utils::isPointNearEdge(scenePos, line1, marginValue)) {
296+
d_ptr->mouseRegion = InEdgeL;
297+
setCursor(Utils::cursorForDirection(line1.angle()));
298+
} else if (Utils::isPointNearEdge(scenePos, line2, marginValue)) {
299+
d_ptr->mouseRegion = InEdgeH;
300+
setCursor(Utils::cursorForDirection(line2.angle()));
301+
} else if (d_ptr->arcPath.contains(scenePos)) {
302+
setMouseRegion(GraphicsBasicItem::MouseRegion::All);
303+
setCursor(Qt::SizeAllCursor);
304+
} else {
305+
d_ptr->mouseRegion = None;
306+
setMouseRegion(GraphicsBasicItem::MouseRegion::None);
307+
return GraphicsBasicItem::MouseRegion::None;
308+
}
309+
310+
setMouseRegion(GraphicsBasicItem::MouseRegion::Edge);
311+
return GraphicsBasicItem::MouseRegion::Edge;
312+
}
313+
315314
} // namespace Graphics

src/graphics/graphicsarcitem.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,13 @@ class GRAPHICS_EXPORT GraphicsArcItem : public GraphicsBasicItem
3232

3333
protected:
3434
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
35-
void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override;
3635

3736
void drawContent(QPainter *painter) override;
3837
void pointsChanged(const QPolygonF &ply) override;
38+
void updateHoverPreview(const QPointF &scenePos) override;
39+
GraphicsBasicItem::MouseRegion detectEdgeRegion(const QPointF &scenePos) override;
3940

4041
private:
41-
void showHoverArc(const QPolygonF &ply);
42-
4342
class GraphicsArcItemPrivate;
4443
QScopedPointer<GraphicsArcItemPrivate> d_ptr;
4544
};

src/graphics/graphicsbasicitem.cpp

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -120,23 +120,6 @@ bool GraphicsBasicItem::showBoundingRect() const
120120
return d_ptr->showBoundingRect;
121121
}
122122

123-
//QVariant BasicGraphicsItem::itemChange(QGraphicsItem::GraphicsItemChange change,
124-
// const QVariant &value)
125-
//{
126-
// if (change == ItemPositionChange && scene()) {
127-
// // value is the new position.
128-
// QPointF newPos = value.toPointF();
129-
// QRectF rect = scene()->sceneRect();
130-
// if (!rect.contains(newPos)) {
131-
// // Keep the item inside the scene rect.
132-
// newPos.setX(qMin(rect.right(), qMax(newPos.x(), rect.left())));
133-
// newPos.setY(qMin(rect.bottom(), qMax(newPos.y(), rect.top())));
134-
// return newPos;
135-
// }
136-
// }
137-
// return QGraphicsItem::itemChange(change, value);
138-
//}
139-
140123
void GraphicsBasicItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
141124
{
142125
if (event->button() != Qt::LeftButton) {
@@ -146,21 +129,33 @@ void GraphicsBasicItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
146129
if (isValid()) {
147130
return;
148131
}
149-
auto pts_tmp = d_ptr->geometryCachePtr->controlPoints();
150-
pts_tmp.append(event->pos());
151-
pointsChanged(pts_tmp);
132+
133+
auto pos = event->pos();
134+
if (!scene()->sceneRect().contains(pos)) {
135+
return;
136+
}
137+
138+
auto controlPoints = d_ptr->geometryCachePtr->controlPoints();
139+
controlPoints.append(pos);
140+
pointsChanged(controlPoints);
152141
}
153142

154143
void GraphicsBasicItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
155144
{
156145
QAbstractGraphicsShapeItem::hoverMoveEvent(event);
146+
147+
auto scenePos = event->scenePos();
148+
if (!scene()->sceneRect().contains(scenePos)) {
149+
return;
150+
}
151+
157152
if (!isValid()) {
153+
updateHoverPreview(scenePos);
158154
return;
159155
}
160156

161157
d_ptr->mouseRegin = MouseRegion::None;
162158
d_ptr->hoveredDotIndex = -1;
163-
const auto scenePos = event->scenePos();
164159
const auto &anchorPoints = d_ptr->geometryCachePtr->controlPoints();
165160
const qreal halfMargin = d_ptr->margin * 0.5;
166161
const QPointF marginOffset(halfMargin, halfMargin);
@@ -177,6 +172,10 @@ void GraphicsBasicItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
177172
}
178173
}
179174

175+
if (detectEdgeRegion(scenePos) == MouseRegion::Edge) {
176+
return;
177+
}
178+
180179
if (shape().contains(scenePos)) {
181180
d_ptr->mouseRegin = MouseRegion::All;
182181
setCursor(Qt::SizeAllCursor);
@@ -284,4 +283,15 @@ void GraphicsBasicItem::setMyCursor(const QPointF &center, const QPointF &pos)
284283
setCursor(Utils::cursorForDirection(angle - 90));
285284
}
286285

286+
void GraphicsBasicItem::updateHoverPreview(const QPointF &scenePos)
287+
{
288+
Q_UNUSED(scenePos);
289+
}
290+
291+
GraphicsBasicItem::MouseRegion GraphicsBasicItem::detectEdgeRegion(const QPointF &scenePos)
292+
{
293+
Q_UNUSED(scenePos);
294+
return MouseRegion::None;
295+
}
296+
287297
} // namespace Graphics

src/graphics/graphicsbasicitem.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ class GRAPHICS_EXPORT GraphicsBasicItem : public QObject, public QAbstractGraphi
4242
bool showBoundingRect() const;
4343

4444
protected:
45-
//QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
4645
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
4746
void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override;
4847
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
@@ -69,6 +68,8 @@ class GRAPHICS_EXPORT GraphicsBasicItem : public QObject, public QAbstractGraphi
6968
void drawShape(QPainter *painter);
7069

7170
virtual void pointsChanged(const QPolygonF &ply) = 0;
71+
virtual void updateHoverPreview(const QPointF &scenePos);
72+
virtual MouseRegion detectEdgeRegion(const QPointF &scenePos);
7273

7374
private:
7475
class GraphicsBasicItemPrivate;

0 commit comments

Comments
 (0)