每次看到QRect的API文档,是不是总觉得那些坐标参数像天书?明明每个单词都认识,组合起来却不知道在实际项目中该怎么用。今天我们不谈枯燥的理论,直接通过5个你会真实遇到的GUI开发场景,把QRect的边界控制、坐标计算和碰撞检测玩出花来。
想象一下,产品经理要求你做一个圆角矩形按钮,点击时要有颜色变化效果。用QRect怎么实现?
首先创建按钮的基本轮廓:
cpp复制QRect buttonRect(20, 20, 120, 50);
QPainterPath path;
path.addRoundedRect(buttonRect, 10, 10);
重点来了——点击检测!很多人会犯的错误是直接用contains()检测鼠标坐标:
cpp复制// 错误示范:忽略了圆角区域
if(buttonRect.contains(event->pos())) {
// 点击处理
}
正确的做法应该考虑圆角矩形的实际点击区域:
cpp复制// 正确做法:使用QPainterPath的contains方法
if(path.contains(event->pos())) {
buttonColor = pressedColor;
} else {
buttonColor = normalColor;
}
update(); // 触发重绘
常见踩坑点:
做截图工具时,鼠标拖拽生成选区是最基础的功能。看看QRect如何优雅处理:
cpp复制// 鼠标按下时记录起点
void startSelection(const QPoint &pos) {
selection.setTopLeft(pos);
selection.setBottomRight(pos); // 初始化为0大小矩形
}
// 鼠标移动时更新选区
void updateSelection(const QPoint &pos) {
selection.setBottomRight(pos);
update(); // 实时刷新界面
}
// 绘制半透明选区
void paintSelection(QPainter &painter) {
painter.setBrush(QColor(100, 100, 255, 50));
painter.drawRect(selection.normalized()); // 确保矩形有效
}
这里有个隐藏技巧:normalized()方法会自动修正矩形坐标,保证width/height为正数。试想用户从右下往左上拖拽时,这个函数就是救命稻草!
实现一个可以选中、移动图形元素的画板,QRect的几何运算就派上大用场了:
cpp复制// 判断点击是否命中某个图形
int findSelectedShape(const QPoint &pos) {
for(int i=0; i<shapes.size(); ++i) {
if(shapes[i].boundingRect().contains(pos)) {
return i;
}
}
return -1;
}
// 移动选中的图形
void moveSelectedShape(const QPoint &offset) {
if(selectedIndex >= 0) {
shapes[selectedIndex].translate(offset.x(), offset.y());
update();
}
}
更复杂的场景——多选功能怎么实现?用intersects()方法检测选择框与图形是否相交:
cpp复制QList<int> getMultiSelected(const QRect &selectionArea) {
QList<int> result;
for(int i=0; i<shapes.size(); ++i) {
if(selectionArea.intersects(shapes[i].boundingRect())) {
result.append(i);
}
}
return result;
}
自动布局是GUI开发的痛点之一,看QRect如何化繁为简。假设要实现类似CSS的flex布局:
cpp复制void calculateLayout(QList<QRect> &items, const QRect &container) {
int totalWidth = 0;
for(const auto &item : items) {
totalWidth += item.width();
}
int spacing = (container.width() - totalWidth) / (items.size() + 1);
int x = container.left() + spacing;
for(auto &item : item) {
item.moveLeft(x);
item.moveTop(container.top() + 10); // 保留顶部边距
x += item.width() + spacing;
}
}
进阶技巧:当窗口大小变化时,如何保持比例布局?
cpp复制// 保存原始比例关系
struct LayoutInfo {
QRect rect;
double leftRatio, topRatio;
double widthRatio, heightRatio;
};
void updateResponsiveLayout(QList<LayoutInfo> &items, const QRect &newContainer) {
for(auto &item : items) {
item.rect.setLeft(newContainer.left() + newContainer.width() * item.leftRatio);
item.rect.setTop(newContainer.top() + newContainer.height() * item.topRatio);
item.rect.setWidth(newContainer.width() * item.widthRatio);
item.rect.setHeight(newContainer.height() * item.heightRatio);
}
}
最后来看个刺激的——用QRect实现2D游戏中的碰撞系统。首先定义游戏对象:
cpp复制class GameObject {
public:
QRect collisionBox;
QPoint velocity;
void updatePosition() {
collisionBox.translate(velocity.x(), velocity.y());
}
bool checkCollision(const GameObject &other) const {
return collisionBox.intersects(other.collisionBox);
}
};
但简单的矩形碰撞太"硬核"了,试试像素级精确检测:
cpp复制bool preciseCollision(const QImage &img1, const QRect &rect1,
const QImage &img2, const QRect &rect2)
{
// 先做快速矩形碰撞检测
if(!rect1.intersects(rect2)) return false;
// 计算相交区域
QRect overlap = rect1.intersected(rect2);
// 像素级检测
for(int y=overlap.top(); y<=overlap.bottom(); ++y) {
for(int x=overlap.left(); x<=overlap.right(); ++x) {
QPoint p1(x - rect1.left(), y - rect1.top());
QPoint p2(x - rect2.left(), y - rect2.top());
if(img1.pixelColor(p1).alpha() > 0 &&
img2.pixelColor(p2).alpha() > 0) {
return true;
}
}
}
return false;
}
性能优化技巧:
united()合并多个碰撞区域进行批量检测QRect的学问远不止这些。当你下次再看到setLeft()和moveLeft()时,不妨想想:这个操作会改变矩形大小吗?不同的构造方式对性能有影响吗?实践出真知,现在就去重构你项目中那些蹩脚的矩形处理代码吧!