电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
【野火i.MX6ULL ARM Linux开发板连载】五子棋开发(一)
分 享
扫描二维码分享
【野火i.MX6ULL ARM Linux开发板连载】五子棋开发(一)
野火
i.MX6ULL
五子棋
瑟寒凌风
关注
发布时间: 2021-04-01
丨
阅读: 1226
# Qt中的2D绘图系统 Qt中提供了强大的2D绘图系统,可以使用相同的API在屏幕和绘图设备上进行绘制,主要基于QPainter、QPaintDevice和QPaintEngine这三个类。其中,QPainter用来执行绘图操作,QPaintDevice提供绘图设备,是一个二维空间的抽象,可以使用QPainter在其上进行绘制,是所有可以进行绘制的对象的基类,它的子类主要有QWidget、QPixmap、QPicture、QImage、QPinter和QOpenGlPaintDevice等。QPaintEngine提供了一些接口,用于QPainter和QPaintDevice内部,使得QPainter可以在不同的设备上进行绘制,可以创建自定义的绘图设备类型,一般编程中不需要使用该类。 绘图系统中,主要通过QPainter来完成具体的绘制操作,其中提供了大量高度优化的函数来完成GUI编程所需要的大部分绘制工作。QPainter可以绘制一些想要的图形,从简单的一条直线到其他任何复杂的图形,还可以绘制文本和图片。QPainter可以在继承QPaintDevice类的任何对象上进行绘制操作。 QPainter一般在一个部件重绘事件(Paint Event)的处理函数paintEvnet()中进行绘制,首先要创建QPainter对象,然后进行图形的绘制,最后销毁QPainter对象。 # 五子棋界面的绘制 五子棋界面的绘制是程序中非常关键的一部分,我们需要使用Qt的2D绘图系统来进行界面的绘制。绘制过程包括棋盘的绘制,棋子的绘制,落子提示的绘制。 由于棋盘是由很多个小正方形组成的,我们可以使用QPainter类的drawRect()函数来绘制很多个并在一起排列的正方形来完成棋盘方格的绘制,同时还要在棋盘的周围画上行数和列数,可以使用drawText()函数,代码如下: ```cpp for (int i = 0; i < BOARD_WIDTH; i++) { painter.drawText(CLU_NUM_START + QPoint(i * CELL_SIZE.width(), 0), QString::number(i + 1)); } for (int i = 0; i < BOARD_HEIGHT; i++) { painter.drawText(ROW_NUM_START + QPoint(0, i * CELL_SIZE.height()), QString::number(i + 1)); } for (int i = 0; i < BOARD_WIDTH - 1; i++) //绘制棋盘格子 { for (int j = 0; j < BOARD_HEIGHT - 1; j++) { painter.drawRect(QRect(START_POS + QPoint(i * CELL_SIZE.width(), j * CELL_SIZE.height()), CELL_SIZE)); } } ``` 棋盘绘制完成了,但是绘制的任务还没有完成,因为我们需要的是一个能够显示当前棋局的棋盘控件。而我们此时仅仅是绘制了一个空的棋盘,没有棋子。现在再把棋子绘制到棋盘上,代码如下所示: ```cpp for (int i = 0; i < BOARD_WIDTH; i++) //绘制棋子 { for (int j = 0; j < BOARD_HEIGHT; j++) { if (board[i][j] != NO_PIECE) { QColor color = (board[i][j] == WHITE_PIECE) ? Qt::white : Qt::black; painter.setBrush(QBrush(color)); painter.drawEllipse(START_POS.x() - CELL_SIZE.width()/2 + i*CELL_SIZE.width(), START_POS.y() - CELL_SIZE.height()/2 + j*CELL_SIZE.height(), CELL_SIZE.width(), CELL_SIZE.height()); } } } ``` 和刚才绘制棋盘类似,只不过设置了画刷的颜色,用来将棋子的内部填充。还有一点要注意的就是棋子的位置,让其的初始位置向左上偏移了半个单元格的长度,使得棋子的中心刚好在棋盘上的交叉线位置上。 绘制完棋盘和棋子之后,差不多就完成了一个五子棋界面的绘制,不过为了更加人性化,我们给五子棋再加入一个落子提示,当用户在棋盘上移动鼠标的时候,给用户指向的落子位置做一个标记,提示用户下棋位置,如下图所示。 ![](https://cf05.ickimg.com/bbsimages/202104/28b9231c94722deb0313f36eef90b415.jpg) ```cpp painter.setPen(Qt::red); QPoint poses[12] = { trackPos + QPoint(0, 8), trackPos, …… trackPos + QPoint(0, 17) }; painter.drawPolyline(poses, 3); painter.drawPolyline(poses + 3, 3); painter.drawPolyline(poses + 6, 3); painter.drawPolyline(poses + 9, 3); ``` 上面的代码中使用了一个trackPos的位置变量,这个变量是用来记录落子提示标记的左上角的位置坐标,我们通过获取当前鼠标的位置来得到这个坐标。重写mouseMoveEvent()函数来以便在鼠标移动时获取鼠标位置,代码如下: ```cpp void BoardWidget::mouseMoveEvent(QMouseEvent *event) { QPoint pos = event->pos() - START_POS + QPoint(CELL_SIZE.width()/2, CELL_SIZE.height()/2); int x = pos.x(); int y = pos.y(); //超过范围 if (x < 0 || x >= CELL_SIZE.width() * BOARD_WIDTH || y < 0 || y >= CELL_SIZE.height() * BOARD_HEIGHT) { return; } int offsetX = x % CELL_SIZE.width(); int offsetY = y % CELL_SIZE.height(); setTrackPos(QPoint(x - offsetX, y - offsetY) + START_POS - QPoint(CELL_SIZE.width()/2, CELL_SIZE.height()/2)); } ``` 上面的setTrackPos()函数是用来设置trackPos变量的值并刷新界面的,同时为了让鼠标不点击的情况下就获取到鼠标的位置,我们需要在构造函数里面调用setMouseTracking()函数,参数值为true。 下面我们再增加一个功能,给特殊棋子标记,比如上一步落子,这样用户可以清晰的分辨出上一步的落子位置,又比如连在一起的五个棋子,显示胜利方的五个棋子所在。这需要我们在落子时记录落子位置,同时在胜利时记录连在一起的五个棋子的位置,下面看标记部分代码: ```cpp painter.setPen(Qt::red); if (lastPos.x() != -1) { QPoint drawPos = START_POS + QPoint(lastPos.x() * CELL_SIZE.width(), lastPos.y() * CELL_SIZE.height()); painter.drawLine(drawPos + QPoint(0, 5), drawPos + QPoint(0, -5)); painter.drawLine(drawPos + QPoint(5, 0), drawPos + QPoint(-5, 0)); } for (QPoint pos : winPoses) { QPoint drawPos = START_POS + QPoint(pos.x() * CELL_SIZE.width(), pos.y() * CELL_SIZE.height()); painter.drawLine(drawPos + QPoint(0, 5), drawPos + QPoint(0, -5)); painter.drawLine(drawPos + QPoint(5, 0), drawPos + QPoint(-5, 0)); } ``` 上面代码中的lastPos是记录的上一个落子位置,在初始化时初始为(-1, -1)表示没有上一个落子。 棋盘控件除了有绘制棋盘的功能,还需要能够接受用户的鼠标点击事件完成下棋。在前面我们已经讲解了如何接受处理Qt的鼠标事件,在这里要做的就是:接受鼠标事件后,判断点击的位置,然后转化为棋子对应二维数组中的位置,然后判断此次为哪一方下棋,更新表示棋盘信息的二维数组,更新界面。这样一个下棋的操作便完成了,具体代码如下所示: ```cpp void BoardWidget::mouseReleaseEvent(QMouseEvent *event) { QPoint pos = event->pos() - START_POS; int x = pos.x(); int y = pos.y(); int pieceX = x / CELL_SIZE.width(); int pieceY = y / CELL_SIZE.height(); int offsetX = x % CELL_SIZE.width(); int offsetY = y % CELL_SIZE.height(); if (offsetX > CELL_SIZE.width() / 2) { pieceX++; } if (offsetY > CELL_SIZE.height() / 2) { pieceY++; } downPiece(pieceX, pieceY); } ``` 在上面的代码中,首先得到点击位置相对于棋盘单元格的起始位置,然后除以单元格的宽和高就得到了棋子在二维数组中的对应位置,这里还进行取余来进行了一次修正,主要是为了使得表示棋盘上某一交叉点位置的范围从每一位置的右下单元格转移到该位置的四周。完成后调用downPiece()函数来进行更新二维数组,刷新界面的任务。 五子棋的规则是哪一方有五个横、竖或斜着的棋子连成一线就算获胜。这样我们判断赢棋就需要判断当前棋盘有哪一方的棋子五个连成一线,最简单的方法就是对棋盘的每个位置判断周围是否有五个同色棋子,代码如下: ```cpp bool fullPieces = true; for (int i = 0; i < BOARD_WIDTH; i++) { for (int j = 0; j < BOARD_HEIGHT; j++) { if (board[i][j] == NO_PIECE) { fullPieces = false; } if (board[i][j] != NO_PIECE && isFivePieceFrom(i, j)) { bool winner = (board[i][j] == WHITE_PIECE) ? WHITE_PLAYER : BLACK_PLAYER; emit gameOver(winner); } } } if (fullPieces) { emit gameOver(2); //代表和棋 } ``` 上面的代码对每一个不为空的位置判断是否周围有五个同色棋子,如果有则发送gameOver()信号并将获胜者作为参数传入,如果没有人获胜并且棋盘已经满了则传入2,代表和棋。其中isFivePieceFrom()函数的定义如下: ```cpp bool BoardWidget::isFivePieceFrom(int x, int y) { return isVFivePieceFrom(x, y) || isHFivePieceFrom(x, y) || isFSFivePieceFrom(x, y) || isBSFivePieceFrom(x, y); } ``` 在isFivePieceFrom()函数中我们又调用了isVFivePieceFrom(),isHFivePieceFrom(),isFSFivePieceFrom()和isBSFivePieceFrom()函数,这些分别是判断垂直,水平,正斜和反斜方向是否有同色五子相连,这里给出isVFivePieceFrom()的函数定义,其他的类似: ```cpp bool BoardWidget::isVFivePieceFrom(int x, int y) { int piece = board[x][y]; for (int i = 1; i < 5; i++) { if (y + i >= BOARD_HEIGHT || board[x][y + i] != piece) { return false; } } winPoses.clear(); for (int i = 0; i < 5; i++) { winPoses.append(QPoint(x, y + i)); } return true; } ``` # 实现双人对战的五子棋 首先构建项目,项目类型为Qt Widget应用,项目名为Gomoku,将窗口基类选为QWidget,窗口类改名为GameWidget,并将创建界面文件取消掉。之后右键点击项目,选择新建文件,选择C++类,类名为BoardWidget,基类选择为QWidget,创建成功后,在BoardWidget类中添加两个保护类型的重载函数: ```cpp protected: void paintEvent(QPaintEvent *event); void mouseReleaseEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); ``` 这里使用paintEvent()函数来绘制棋盘界面,mouseRelaseEvetn()函数主要用来获取用户的点击输入,mouseMoveEvent()用来监视鼠标移动。接下来继续添加8个私有函数: ```cpp private: void initBoard(); void downPiece(int x, int y); void checkWinner(); bool isFivePieceFrom(int x, int y); //判断从(x, y)处开始,是否有五个同色棋子在一条线上 bool isVFivePieceFrom(int x, int y); //判断从(x, y)处开始,向下是否有五个同色棋子 bool isHFivePieceFrom(int x, int y); //判断从(x, y)处开始,向下是否有五个同色棋子 bool isFSFivePieceFrom(int x, int y); //判断从(x, y)处开始,右上方向是否有五个同色棋子 bool isBSFivePieceFrom(int x, int y); //判断(x, y)处开始, 右下方向是否有五个同色棋子 void setTrackPos(const QPoint &value); ``` 其中initBoard()函数用来初始化棋盘数据,类里面用了一个二维数组来储存了棋盘信息。downPiece()函数用来在指定位置落子,同时会刷新界面。checkWinner()用来检查谁获胜了,后面几个函数都是用来检查的,如果有人获胜了,会发出一个gameOver()信号。下面添加信号声明: ```cpp bool isBSFivePieceFrom(int x, int y); //判断(x, y)处开始, 右下方向是否有五个同色棋子 void setTrackPos(const QPoint &value); ``` 其中initBoard()函数用来初始化棋盘数据,类里面用了一个二维数组来储存了棋盘信息。downPiece()函数用来在指定位置落子,同时会刷新界面。checkWinner()用来检查谁获胜了,后面几个函数都是用来检查的,如果有人获胜了,会发出一个gameOver()信号。下面添加信号声明: ```cpp signals: void gameOver(int winner); ``` 下面添加一些程序中用到的一些常量数据的声明,它们声明为public的静态常量,同时记得要在boardwidget.cpp中定义。 ```cpp public: static const QSize WIDGET_SIZE; static const QSize CELL_SIZE; static const QPoint START_POS; static const QPoint ROW_NUM_START; static const QPoint CLU_NUM_START; static const int BOARD_WIDTH = 15; static const int BOARD_HEIGHT = 15; static const int NO_PIECE = 0; static const int WHITE_PIECE = 1; static const int BLACK_PIECE = 2; static const bool WHITE_PLAYER = true; static const bool BLACK_PLAYER = false; ``` 下面添加一些私有变量的声明: ```cpp private: bool endGame; int board[BOARD_WIDTH][BOARD_HEIGHT]; int nextPlayer; QPoint lastPos; QPoint trackPos; QVector
winPoses; ``` 现在完成了声明,转到boardwidget.cpp文件,先添加
和
两个头文件。具体的静态常量以及成员函数的定义如下: ```cpp /@@*类静态数据成员定义*/ const QSize BoardWidget::WIDGET_SIZE(430, 430); const QSize BoardWidget::CELL_SIZE(25, 25); const QPoint BoardWidget::START_POS(40, 40); const QPoint BoardWidget::ROW_NUM_START(15, 45); const QPoint BoardWidget::CLU_NUM_START(39, 25); const int BoardWidget::BOARD_WIDTH; const int BoardWidget::BOARD_HEIGHT; const int BoardWidget::NO_PIECE; const int BoardWidget::WHITE_PIECE; const int BoardWidget::BLACK_PIECE; const bool BoardWidget::WHITE_PLAYER; const bool BoardWidget::BLACK_PLAYER; BoardWidget::BoardWidget(QWidget *parent) : QWidget(parent), trackPos(28, 28) { setFixedSize(WIDGET_SIZE); setMouseTracking(true); initBoard(); } void BoardWidget::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.fillRect(0, 0, width(), height(), Qt::gray); //背景颜色 for (int i = 0; i < BOARD_WIDTH; i++) { painter.drawText(CLU_NUM_START + QPoint(i * CELL_SIZE.width(), 0), QString::number(i + 1)); } for (int i = 0; i < BOARD_HEIGHT; i++) { painter.drawText(ROW_NUM_START + QPoint(0, i * CELL_SIZE.height()), QString::number(i + 1)); } for (int i = 0; i < BOARD_WIDTH - 1; i++) //绘制棋盘格子 { for (int j = 0; j < BOARD_HEIGHT - 1; j++) { painter.drawRect(QRect(START_POS + QPoint(i * CELL_SIZE.width(), j * CELL_SIZE.height()), CELL_SIZE)); } } painter.setPen(Qt::red); QPoint poses[12] = { trackPos + QPoint(0, 8), trackPos, trackPos + QPoint(8, 0), trackPos + QPoint(17, 0), trackPos + QPoint(25, 0), trackPos + QPoint(25, 8), trackPos + QPoint(25, 17), trackPos + QPoint(25, 25), trackPos + QPoint(17, 25), trackPos + QPoint(8, 25), trackPos + QPoint(0, 25), trackPos + QPoint(0, 17) }; painter.drawPolyline(poses, 3); painter.drawPolyline(poses + 3, 3); painter.drawPolyline(poses + 6, 3); painter.drawPolyline(poses + 9, 3); painter.setPen(Qt::NoPen); painter.setPen(Qt::NoPen); for (int i = 0; i < BOARD_WIDTH; i++) //绘制棋子 { for (int j = 0; j < BOARD_HEIGHT; j++) { if (board[i][j] != NO_PIECE) { QColor color = (board[i][j] == WHITE_PIECE) ? Qt::white : Qt::black; painter.setBrush(QBrush(color)); painter.drawEllipse(START_POS.x() - CELL_SIZE.width()/2 + i*CELL_SIZE.width(), START_POS.y() - CELL_SIZE.height()/2 + j*CELL_SIZE.height(), CELL_SIZE.width(), CELL_SIZE.height()); } } } painter.setPen(Qt::red); if (lastPos.x() != -1) { QPoint drawPos = START_POS + QPoint(lastPos.x() * CELL_SIZE.width(), lastPos.y() * CELL_SIZE.height()); painter.drawLine(drawPos + QPoint(0, 5), drawPos + QPoint(0, -5)); painter.drawLine(drawPos + QPoint(5, 0), drawPos + QPoint(-5, 0)); } for (QPoint pos : winPoses) { QPoint drawPos = START_POS + QPoint(pos.x() * CELL_SIZE.width(), pos.y() * CELL_SIZE.height()); painter.drawLine(drawPos + QPoint(0, 5), drawPos + QPoint(0, -5)); painter.drawLine(drawPos + QPoint(5, 0), drawPos + QPoint(-5, 0)); } } void BoardWidget::mouseReleaseEvent(QMouseEvent *event) { if (!endGame) { QPoint pos = event->pos() - START_POS; int x = pos.x(); int y = pos.y(); int pieceX = x / CELL_SIZE.width(); int pieceY = y / CELL_SIZE.height(); int offsetX = x % CELL_SIZE.width(); int offsetY = y % CELL_SIZE.height(); if (offsetX > CELL_SIZE.width() / 2) { pieceX++; } if (offsetY > CELL_SIZE.height() / 2) { pieceY++; } downPiece(pieceX, pieceY); } } void BoardWidget::mouseMoveEvent(QMouseEvent *event) { QPoint pos = event->pos() - START_POS + QPoint(CELL_SIZE.width()/2, CELL_SIZE.height()/2); int x = pos.x(); int y = pos.y(); //超过范围 if (x < 0 || x >= CELL_SIZE.width() * BOARD_WIDTH || y < 0 || y >= CELL_SIZE.height() * BOARD_HEIGHT) { return; } int offsetX = x % CELL_SIZE.width(); int offsetY = y % CELL_SIZE.height(); setTrackPos(QPoint(x - offsetX, y - offsetY) + START_POS - QPoint(CELL_SIZE.width()/2, CELL_SIZE.height()/2)); } void BoardWidget::initBoard() { for (int i = 0; i < BOARD_WIDTH; i++) { for (int j = 0; j < BOARD_HEIGHT; j++) { board[i][j] = NO_PIECE; } } lastPos = QPoint(-1, -1); endGame = false; winPoses.clear(); nextPlayer = BLACK_PLAYER; } void BoardWidget::downPiece(int x, int y) { if (x >= 0 && x < BOARD_WIDTH && y >= 0 && y < BOARD_HEIGHT && board[x][y] == NO_PIECE) { board[x][y] = (nextPlayer == WHITE_PLAYER) ? WHITE_PIECE : BLACK_PIECE; nextPlayer = !nextPlayer; lastPos = QPoint(x, y); checkWinner(); update(); } } void BoardWidget::checkWinner() { bool fullPieces = true; for (int i = 0; i < BOARD_WIDTH; i++) { for (int j = 0; j < BOARD_HEIGHT; j++) { if (board[i][j] == NO_PIECE) { fullPieces = false; } if (board[i][j] != NO_PIECE && isFivePieceFrom(i, j)) { bool winner = (board[i][j] == WHITE_PIECE) ? WHITE_PLAYER : BLACK_PLAYER; endGame = true; emit gameOver(winner); } } } if (fullPieces) { endGame = true; emit gameOver(2); //代表和棋 } } bool BoardWidget::isFivePieceFrom(int x, int y) { return isVFivePieceFrom(x, y) || isHFivePieceFrom(x, y) || isFSFivePieceFrom(x, y) || isBSFivePieceFrom(x, y); } bool BoardWidget::isVFivePieceFrom(int x, int y) { int piece = board[x][y]; for (int i = 1; i < 5; i++) { if (y + i >= BOARD_HEIGHT || board[x][y + i] != piece) { return false; } } winPoses.clear(); for (int i = 0; i < 5; i++) { winPoses.append(QPoint(x, y + i)); } return true; } bool BoardWidget::isHFivePieceFrom(int x, int y) { int piece = board[x][y]; for (int i = 1; i < 5; i++) { if (x + i >= BOARD_WIDTH || board[x + i][y] != piece) { return false; } } winPoses.clear(); for (int i = 0; i < 5; i++) { winPoses.append(QPoint(x + i, y)); } return true; } bool BoardWidget::isFSFivePieceFrom(int x, int y) { int piece = board[x][y]; for (int i = 1; i < 5; i++) { if (x + i >= BOARD_WIDTH || y - i < 0 || board[x + i][y - i] != piece) { return false; } } winPoses.clear(); for (int i = 0; i < 5; i++) { winPoses.append(QPoint(x + i, y - i)); } return true; } bool BoardWidget::isBSFivePieceFrom(int x, int y) { int piece = board[x][y]; for (int i = 1; i < 5; i++) { if (x + i >= BOARD_WIDTH || y + i >= BOARD_HEIGHT || board[x + i][y + i] != piece) { return false; } } winPoses.clear(); for (int i = 0; i < 5; i++) { winPoses.append(QPoint(x + i, y + i)); } return true; } void BoardWidget::setTrackPos(const QPoint &value) { trackPos = value; update(); } ``` 完成了BoardWidget类,现在需要完成GameWidget类,这次的实训只需要在GameWidget类中添加一个BoardWidget类的私有成员即可,之后更复杂的功能在这里添加即可。我们首先需要添加”boardwidget.h”头文件,记得这次是双引号。之后添加一个私有的showWinner()函数显示胜利者和一个BoardWidget私有成员。如下: ```cpp private: void showWinner(int winner); private: BoardWidget *boardWidget; 转到gamewidget.cpp文件,更改构造函数的实现并添加showWinner()函数的实现如下: GameWidget::GameWidget(QWidget *parent) : QWidget(parent) { setWindowTitle("五子棋"); QVBoxLayout *mainLayout = new QVBoxLayout(this); boardWidget = new BoardWidget(this); QHBoxLayout *hLayout = new QHBoxLayout(); QPushButton *newGame = new QPushButton("重新开始"); hLayout->addWidget(newGame); hLayout->addStretch(); mainLayout->addLayout(hLayout); mainLayout->addWidget(boardWidget); connect(newGame, &QPushButton::clicked, boardWidget, &BoardWidget::initBoard); connect(boardWidget, &BoardWidget::gameOver, this, &GameWidget::showWinner); } GameWidget::~GameWidget() { } void GameWidget::showWinner(int winner) { if (winner != 2) { QString playerName = (winner == BoardWidget::WHITE_PLAYER) ? "白方" : "黑方"; QMessageBox::information(this, "游戏结束", tr("恭喜%1获胜!!").arg(playerName), QMessageBox::Ok); } else { QMessageBox::information(this, "游戏结束", "和棋!", QMessageBox::Ok); } } ``` 运行程序,查看程序效果 ![](https://cf05.ickimg.com/bbsimages/202104/b91ee7807675a1931a4b781a56b44f60.jpg) ![](https://cf05.ickimg.com/bbsimages/202104/549752a38118ecbc1d721fe1fd09531f.jpg)
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
0
)
瑟寒凌风
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交