我正在编写一个图形编辑器程序。在程序中,我只能绘制两种形状:圆形和矩形。我能够正确选择并拖动矩形,但对于圆形,情况并非如此。如果我的鼠标位于圆圈的左上角及上方,我只能选择并拖动圆圈。
我试图找到contains方法中的逻辑错误,但没有成功。 这是代码:
#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 dx = x - m_x;
int dy = y - m_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() {
auto* window = new QWidget;
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();
}
我通过更改 Circle 类中的 contains 函数来修复它。 现在看起来像这样:
[[nodiscard]] bool contains(int x, int y) const override {
int dx = x - m_x-25;
int dy = y - m_y-25;
return (dx * dx + dy * dy) <= (m_radius * m_radius);
}