Qt图形编辑器圆形选中拖拽异常问题排查求助
Qt图形编辑器圆形选中异常修复
问题
使用Qt开发图形编辑器,支持绘制矩形和圆形。矩形可正常选中拖拽,但圆形仅在鼠标处于其左上角区域及上方时才能被选中拖拽,排查contains方法未发现明显逻辑错误。
错误原因
问题出在圆形的坐标定义不一致:
circle类的paint方法调用painter.drawEllipse(m_x, m_y, 2*m_radius, 2*m_radius),Qt的drawEllipse前两个参数是外接矩形的左上角坐标,而非圆心。- 但
contains方法中,错误地将m_x、m_y当作圆心坐标计算鼠标点到圆心的距离,导致实际判定的"圆形区域"是以外接矩形左上角为圆心的圆,和绘制出的圆形错位。
修复方案
调整contains方法,使用正确的圆心坐标计算:圆心应为外接矩形左上角坐标加上半径,即m_x + m_radius和m_y + m_radius。
修改后的circle类contains方法
[[nodiscard]] bool contains(int x, int y) const override { // 计算正确的圆心坐标:外接矩形左上角 + 半径 int center_x = m_x + m_radius; int center_y = m_y + m_radius; int dx = x - center_x; int dy = y - center_y; return (dx * dx + dy * dy) <= (m_radius * m_radius); }
另一种可选方案(统一坐标定义为圆心)
如果希望将m_x、m_y统一作为圆心坐标,可同时修改circle的构造和paint方法:
// circle构造函数 circle(int x, int y, int radius): shape(x, y), m_radius{radius} { } // paint方法 void paint(QPainter& painter) const override { painter.setBrush(QColor(255, 255, 0)); is_selected ? painter.setPen(QColor(255, 0, 0)) : painter.setPen(Qt::NoPen); // 外接矩形左上角 = 圆心 - 半径 painter.drawEllipse(m_x - m_radius, m_y - m_radius, 2*m_radius, 2*m_radius); } // contains方法保持原逻辑即可 [[nodiscard]] bool contains(int x, int y) const override { int dx = x - m_x; int dy = y - m_y; return (dx * dx + dy * dy) <= (m_radius * m_radius); }
同时需要修改创建圆形的代码,将圆心设为鼠标位置:
// 在QShapeCanvas的mousePressEvent中 addShape(std::make_shared<circle>(event->pos().x(), event->pos().y(), 50));
完整修正代码(采用第一种方案)
#include <iostream> #include <string> #include <sstream> #include <vector> #include <memory> #include <QWidget> #include <QApplication> #include <QSizePolicy> #include <QPainter> #include <QMouseEvent> #include <QObject> #include <QBoxLayout> #include <QColor> #include <QPushButton> class QShapeCanvas; class shape { public: virtual ~shape() = default; virtual void paint(QPainter& canvas) const = 0; [[nodiscard]] virtual bool contains(int x, int y) const = 0; void move(int dx, int dy) { m_x += dx; m_y += dy; } explicit operator std::string() const { std::ostringstream oss; to_string(oss); return oss.str(); } void select() { is_selected = true; } void unselect() { is_selected = false; } [[nodiscard]] int get_x() const{ return m_x; } [[nodiscard]] int get_y() const{ return m_y; } void set_x(int x){ m_x = x; } void set_y(int y){ m_y = y; } protected: int m_x, m_y; bool is_selected; shape(int x, int y): m_x{x}, m_y{y}, is_selected{false}{} virtual void to_string(std::ostringstream &oss) const = 0; }; class rectangle : public shape { public: rectangle(int x, int y, int width, int height): shape(x, y), m_width{width}, m_height{height} { } void paint(QPainter& painter) const override { painter.setBrush(QColor(255, 0, 255)); is_selected ? painter.setPen(QColor(255, 0, 0)) : painter.setPen(Qt::NoPen); painter.drawRect(m_x, m_y, m_width, m_height); } [[nodiscard]] bool contains(int x, int y) const override { return x >= m_x && x < (m_x + m_width) && y >= m_y && y < (m_y + m_height); } protected: void to_string(std::ostringstream &oss) const override { oss << "{ \"name\": \"rectangle\", \"x\": " << m_x << ", \"y\": " << m_y << ", \"width\": " << m_width << ", \"height\": " << m_height << " }"; } private: int m_width, m_height; }; class circle : public shape { public: circle(int x, int y, int radius): shape(x, y), m_radius{radius} { } void paint(QPainter& painter) const override { painter.setBrush(QColor(255, 255, 0)); is_selected ? painter.setPen(QColor(255, 0, 0)) : painter.setPen(Qt::NoPen); painter.drawEllipse(m_x, m_y, 2*m_radius, 2*m_radius); } [[nodiscard]] bool contains(int x, int y) const override { // 修正:使用外接矩形左上角+半径作为圆心 int center_x = m_x + m_radius; int center_y = m_y + m_radius; int dx = x - center_x; int dy = y - center_y; return (dx * dx + dy * dy) <= (m_radius * m_radius); } protected: void to_string(std::ostringstream &oss) const override { oss << "{ \"name\": \"circle\", \"x\": " << m_x << ", \"y\": " << m_y << ", \"radius\": " << m_radius << " }"; } private: const int m_radius; }; class QShapeCanvas : public QWidget { public: enum ShapesToDraw { CircleShape, RectangleShape, NoneShape }; explicit QShapeCanvas(QWidget* parent = nullptr): QWidget(parent) { setMinimumSize(200,200); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setFocusPolicy(Qt::StrongFocus); next_shape_to_draw = NoneShape; selected_shape = nullptr; is_in_dragging_mode = false; } void addShape(const std::shared_ptr<shape>& new_shape) { shapes.push_back(new_shape); } void setNextShapeToDraw(ShapesToDraw to_draw) { next_shape_to_draw = to_draw; } protected: void paintEvent(QPaintEvent *) override { QPainter painter; painter.begin(this); for(auto& shape : shapes) { shape->paint(painter); } painter.end(); } void keyPressEvent(QKeyEvent *event) override { if (selected_shape && event->key() == Qt::Key_Delete) { selected_shape->unselect(); for (auto it = shapes.begin(); it != shapes.end(); it++) { if ((*it) == selected_shape) { shapes.erase(it); selected_shape = nullptr; break; } } update(); } } void mousePressEvent(QMouseEvent *event) override { if (selected_shape) { selected_shape->unselect(); selected_shape = nullptr; } if (event->button() == Qt::LeftButton) { if (next_shape_to_draw == RectangleShape) { addShape(std::make_shared<rectangle>(event->pos().x() - 25, event->pos().y() - 25, 50, 50)); } else if (next_shape_to_draw == CircleShape) { addShape(std::make_shared<circle>(event->pos().x() - 50, event->pos().y() - 50, 50)); } update(); } else if (event->button() == Qt::RightButton) { for (auto it = shapes.rbegin(); it != shapes.rend(); it++) { if ((*it)->contains(event->pos().x(), event->pos().y())) { selected_shape = *it; (*it)->select(); is_in_dragging_mode = true; previous_x = event->pos().x(); previous_y = event->pos().y(); break; } } update(); } } void mouseMoveEvent(QMouseEvent *event) override{ if(selected_shape){ int dx = - previous_x + event->pos().x(); int dy = - previous_y + event->pos().y(); selected_shape->move(dx,dy); previous_x = event->pos().x(); previous_y = event->pos().y(); update(); } } private: std::vector<std::shared_ptr<shape>> shapes; ShapesToDraw next_shape_to_draw; std::shared_ptr<shape> selected_shape; bool is_in_dragging_mode = false; int previous_x, previous_y; }; class QPaintWindow : public QWidget { public: QPaintWindow() { this->setWindowTitle("Problem #5"); this->resize(500, 500); auto* canvas = new QShapeCanvas(this); canvas->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); canvas->setFocusPolicy(Qt::StrongFocus); auto* toolbar = new QWidget; auto* circleButton = new QPushButton("Circle", toolbar); circleButton->setStyleSheet("background-color: white;" "color: black;"); auto* rectangleButton = new QPushButton("Rectangle", toolbar); rectangleButton->setStyleSheet("background-color: white;" "color: black;"); QObject::connect(rectangleButton, &QPushButton::clicked, [canvas, rectangleButton, circleButton] { rectangleButton->setStyleSheet("background-color: red;" "color: white;"); circleButton->setStyleSheet("background-color: white;" "color: black;"); canvas->setNextShapeToDraw(QShapeCanvas::ShapesToDraw::RectangleShape); }); QObject::connect(circleButton, &QPushButton::clicked, [canvas, circleButton, rectangleButton] { rectangleButton->setStyleSheet("background-color: white;" "color: black;"); circleButton->setStyleSheet("background-color: red;" "color: white;"); canvas->setNextShapeToDraw(QShapeCanvas::ShapesToDraw::CircleShape); }); auto* button_layout = new QHBoxLayout(toolbar); button_layout->addWidget(circleButton); button_layout->addWidget(rectangleButton); auto* window_layout = new QVBoxLayout(this); window_layout->addWidget(canvas); window_layout->addWidget(toolbar); } }; int main(int argc, char *argv[]) { QApplication app(argc, argv); QPaintWindow window; window.show(); return QApplication::exec(); }
内容的提问来源于stack exchange,提问作者ruslan
相关产品推荐
相关产品推荐

