C++学习笔记之鼠标绘图

2 篇文章 1 订阅

今天我所要分享的内容为如何在Qt中编写鼠标绘图的程序。

绘制的图形可以有直线、矩形、椭圆、多边形、自由曲线。

当然还在其中添加了一个扫描线算法,对自己绘制的多边形进行填充,不过还有一两点特殊情况没有考虑清楚,导致填充不完整。不过其他画图的程序都是没有问题的。Qt中有自带的填充算法,所以我用QPainterPath绘制自由曲线时,自动填充没有问题。

主程序如下:

#include "minidraw.h"
#include <QtWidgets/QApplication>
#include <QLabel>
#include "viewwidget.h"
int main(int argc, char *argv[])
{
	_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);//检查内存泄漏

	QApplication a(argc, argv);
	MiniDraw  mini_draw_action_;
	mini_draw_action_.show();
	mini_draw_action_.setMinimumSize(600, 400);
	return a.exec();

}

Qt界面按钮设置的H文件和C文件:

H文件如下:


#ifndef MINIDRAW_H
#define MINIDRAW_H

#include <QtWidgets/QMainWindow>
#include "ui_minidraw.h"
#include "viewwidget.h"
class MiniDraw : public QMainWindow
{
	Q_OBJECT

public:
	MiniDraw(QWidget *parent = 0);
	~MiniDraw();
	void Init();

	void set_figure_type_to_line();
	void set_figure_type_to_ellipse();
	void set_figure_type_to_rectangle();
	void set_figure_type_to_polygon();
	void set_figure_type_to_freehand();
	void set_figure_type_to_delete();
	void set_figure_type_to_color();
	void set_figure_type_to_freehan1();
	void color_filling();

public:
	Ui::MiniDrawClass ui;
	viewwidget*	view_widget_; //声明viewwidget指针
	QAction* mini_draw_action_;  //声明动作
	QMenu* main_menu_;          //声明菜单
	QToolBar* main_toolbar_;   //声明工具栏

	void CreateButtons();  //声明函数

};

#endif // MINIDRAW_H


C文件如下:


#include "minidraw.h"
#include <QMessageBox>
#include <QColorDialog>
#include "viewwidget.h"
#include "figures.h"
MiniDraw::MiniDraw(QWidget *parent)
: QMainWindow(parent)
{
	ui.setupUi(this);
	CreateButtons();
	Init();
}

MiniDraw::~MiniDraw()
{
}

void MiniDraw::CreateButtons()
{

	main_menu_ = menuBar()->addMenu(tr("&Figure Tool"));  //创建菜单,标签为Figure Tool
	main_toolbar_ = addToolBar(tr("&Figure Tool"));     //创建工具栏,标签为Figure Tool

	mini_draw_action_ = new QAction(tr("&Line"), this);  //&的作用:设置此动作的快捷键为标签字符串的首字母,此例为’L’
	main_menu_->addAction(mini_draw_action_);            //将动作Line加载到Main菜单中
	main_toolbar_->addAction(mini_draw_action_);       //将动作Line加载到Main工具栏中
	connect(mini_draw_action_, &QAction::triggered,this, &MiniDraw::set_figure_type_to_line);

 	mini_draw_action_ = new QAction(tr("&Ellipse"), this);  //&的作用:设置此动作的快捷键为标签字符串的首字母,此例为’E’
 	main_menu_->addAction(mini_draw_action_);            //将动作Ellipse加载到Main菜单中
 	main_toolbar_->addAction(mini_draw_action_);       //将动作Ellipse加载到Main工具栏中
	connect(mini_draw_action_, &QAction::triggered, this, &MiniDraw::set_figure_type_to_ellipse);

	mini_draw_action_ = new QAction(tr("&Rectangle"), this);  //&的作用:设置此动作的快捷键为标签字符串的首字母,此例为’R’
	main_menu_->addAction(mini_draw_action_);            //将动作Rectangle加载到Main菜单中
	main_toolbar_->addAction(mini_draw_action_);       //将动作Rectangle加载到Main工具栏中
	connect(mini_draw_action_, &QAction::triggered, this, &MiniDraw::set_figure_type_to_rectangle);

	mini_draw_action_ = new QAction(tr("&Polygon"), this);  //&的作用:设置此动作的快捷键为标签字符串的首字母,此例为’P’
	main_menu_->addAction(mini_draw_action_);            //将动作Polygon加载到Main菜单中
	main_toolbar_->addAction(mini_draw_action_);       //将动作Polygon加载到Main工具栏中
	connect(mini_draw_action_, &QAction::triggered, this, &MiniDraw::set_figure_type_to_polygon);

	mini_draw_action_ = new QAction(tr("&Freehand"), this);  
	main_menu_->addAction(mini_draw_action_);            //将动作Freehand加载到Main菜单中
	main_toolbar_->addAction(mini_draw_action_);       //将动作Freehand加载到Main工具栏中
	connect(mini_draw_action_, &QAction::triggered, this, &MiniDraw::set_figure_type_to_freehand);

	mini_draw_action_ = new QAction(tr("&Freehand1"), this);
	main_menu_->addAction(mini_draw_action_);            //将动作Freehand1加载到Main菜单中
	main_toolbar_->addAction(mini_draw_action_);       //将动作Freehand1加载到Main工具栏中
	connect(mini_draw_action_, &QAction::triggered, this, &MiniDraw::set_figure_type_to_freehan1);

	mini_draw_action_ = new QAction(tr("&Delete"), this);  
	main_menu_->addAction(mini_draw_action_);            //将动作Delete加载到Main菜单中
	main_toolbar_->addAction(mini_draw_action_);       //将动作Delete加载到Main工具栏中
	connect(mini_draw_action_, &QAction::triggered, this, &MiniDraw::set_figure_type_to_delete);

	mini_draw_action_ = new QAction(tr("&Color"), this);
	main_menu_->addAction(mini_draw_action_);            //将动作设置颜色加载到Main菜单中
	main_toolbar_->addAction(mini_draw_action_);       //将动作设置颜色加载到Main工具栏中
	connect(mini_draw_action_, &QAction::triggered, this, &MiniDraw::set_figure_type_to_color);

	mini_draw_action_ = new QAction(tr("&Color_Fill"), this);
	main_menu_->addAction(mini_draw_action_);         //将动作颜色填充加载到主菜单中
	main_toolbar_->addAction(mini_draw_action_);
	connect(mini_draw_action_, &QAction::triggered, this, &MiniDraw::color_filling);

}

void MiniDraw::Init()
{
	view_widget_ = new viewwidget();
	setCentralWidget(view_widget_);
	//实例代码中将绘图控件的实例化放在Init函数中
	//Init函数将在MiniDraw的构造函数中被调用
	//实例化viewwidgett控件窗口
	//将viewwidget控件设置为主窗口的中心位置
}

void MiniDraw::set_figure_type_to_line()
{
	view_widget_->figure_type_ = viewwidget::kLine;
}

void MiniDraw::set_figure_type_to_ellipse()
{
	view_widget_->figure_type_ = viewwidget::kEllipse;
}

void MiniDraw::set_figure_type_to_rectangle()
{
	view_widget_->figure_type_ = viewwidget::kRectangle;
}

void MiniDraw::set_figure_type_to_polygon()
{
	view_widget_->figure_type_ = viewwidget::kPolygon;
}

void MiniDraw::set_figure_type_to_freehand()
{
	view_widget_->figure_type_ = viewwidget::kFreehand;
}

void MiniDraw::set_figure_type_to_freehan1()
{
	view_widget_->figure_type_ = viewwidget::kFreehand1;
}

void MiniDraw::set_figure_type_to_delete()
{
	if (!view_widget_->figure_array_.empty())
	{
		view_widget_->figure_array_.pop_back();
	}
}

void MiniDraw::set_figure_type_to_color()
{
	view_widget_->color = QColorDialog::getColor(Qt::white, this);
}

void MiniDraw::color_filling()
{
	view_widget_->figure_type_ = viewwidget::kColorFill;
}


具体的鼠标响应函数的声明和定义:

H文件如下:

#ifndef VIEWWIDGET_H
#define VIEWWIDGET_H

#include <QWidget>
#include <QPainterPath>
#include "ui_viewwidget.h"
#include <QMouseEvent>
#include <vector>
#include <iostream>
#include<QColor>
#include "figures.h"
class viewwidget : public QWidget
{
	Q_OBJECT

public:
	viewwidget(QWidget *parent = 0);  //绘制函数声明,Qt所有的绘制都只能在此函数中完成
	~viewwidget();
	QColor color;
	enum CFigureType{ kLine = 0, kRectangle = 1, kEllipse = 2, kPolygon = 3,kFreehand=4,kColorFill=5,kFreehand1};
	std::vector <CFigures*>figure_array_;
	CFigureType figure_type_;

protected:
	void paintEvent(QPaintEvent *);
	void mousePressEvent(QMouseEvent *event);        //鼠标击发响应函数(左右键,单双击)
	void mouseMoveEvent(QMouseEvent *event);        //鼠标移动响应函数
	void mouseReleaseEvent(QMouseEvent *event);    //鼠标释放响应函数(左右键,单双击)
	void colorFilling(std::vector<QPoint> ThePolygon,QPainter &paint);
	int calculate_x_intersect(QPoint point_a, QPoint point_b, int y);
public:
	Ui::viewwidget ui;
	bool draw_status_;
	//当前绘制状态,true为绘制当前鼠标拖动的图元,false为不绘制
	QPoint	start_point_;
	//当前图元的起始点
	QPoint	end_point_;
	//当前图元的终止点
	QPoint current_point_;

	std::vector<QPoint>points;  //保存鼠标经过的点
	QPainterPath *path;

};

#endif // VIEWWIDGET_H

C文件如下:


#include "viewwidget.h"
#include <QMouseEvent>
#include <QPaintEvent>
#include <QPainter>
#include <QPoint>
#include <QScreen>
#include <QPixmap>
#include <QImage>
#include <iostream>
#include <QColor>
#include <vector>
#include <QColorDialog>
#include <QMessageBox>
#include <algorithm>
#include "figure.h"
viewwidget::viewwidget(QWidget *parent)
: QWidget(parent)
{
	ui.setupUi(this);
	draw_status_ = false;  //设置初始绘制状态为 – 不绘制
	this->path = NULL;
}

viewwidget::~viewwidget()
{
	for (size_t i = 0; i < figure_array_.size(); i++)
	{
		if (figure_array_[i])
		{
			delete figure_array_[i];
			figure_array_[i] = NULL;
		}
	}

}
//鼠标击发响应函数函数体
void viewwidget::mousePressEvent(QMouseEvent *event)
{
	if (Qt::LeftButton == event->button())               //判断是否是鼠标左击
	{
		draw_status_ = true;                        //设置绘制状态为 – 绘制
		start_point_ = end_point_ = event->pos();  //将图元初始点设置为当前鼠标击发点

		if (figure_type_==kPolygon)
		{
			points.push_back(start_point_);
		}
		if (figure_type_==kFreehand)
		{
			path = new QPainterPath;
			path->moveTo(event->pos());
		}
		if (figure_type_==kFreehand1)
		{
			points.push_back(start_point_);
		}
	}
}
//鼠标移动响应函数函数体
void viewwidget::mouseMoveEvent(QMouseEvent *event)
{
	if (draw_status_)    //判断当前绘制状态
	{
		end_point_ = event->pos();  //若为真,则设置图元终止点为鼠标当前位置
	}
	if (figure_type_ == kFreehand)
	{
		path->lineTo(event->pos());
	}
	if (figure_type_==kFreehand1)
	{
		points.push_back(start_point_);
	}
}
//鼠标释放响应函数函数体
void viewwidget::mouseReleaseEvent(QMouseEvent *event)
{
 	CFigures* current_figure_ = NULL;
 	switch(figure_type_)
 	{
 	case kLine:
 		current_figure_ = new Line(start_point_.rx(), start_point_.ry(), end_point_.rx(), end_point_.ry());
		current_figure_->color3 = color;
 		figure_array_.push_back(current_figure_);
 		draw_status_ = false; //设置绘制状态为 – 不绘制
 		break;
 	case kEllipse:
 		current_figure_ = new Ellipse(start_point_.rx(), start_point_.ry(), end_point_.rx(), end_point_.ry());
		current_figure_->color3 = color;
  		figure_array_.push_back(current_figure_);
 		draw_status_ = false; //设置绘制状态为 – 不绘制
 		break;
 	case kPolygon:
		if (Qt::RightButton==event->button())
		{
			current_figure_ = new Polygon(points);
			current_figure_->color3 = color;
			figure_array_.push_back(current_figure_);
			//points.clear();    //清除这些点,若不用进行颜色填充则不用注释
			//draw_status_ = false; //设置绘制状态为 – 不绘制
		}
		break;
	case kRectangle:
		current_figure_ = new Rectangle(start_point_.rx(), start_point_.ry(), end_point_.rx(), end_point_.ry());
		current_figure_->color3 = color;
		figure_array_.push_back(current_figure_);
		draw_status_ = false; //设置绘制状态为 – 不绘制
		break;  //必不可少
	case kFreehand:
		current_figure_ = new Freehand(*path);
		current_figure_->color3 = color;
		figure_array_.push_back(current_figure_);
		draw_status_ = false;
		delete path;
		path = NULL;
		break;
	case  kFreehand1:
		if (Qt::RightButton == event->button())
		{
			current_figure_ = new Polygon(points);
			current_figure_->color3 = color;
			figure_array_.push_back(current_figure_);
			//points.clear(); //清除这些点,若不用进行颜色填充则不用注释
			//draw_status_ = false; //设置绘制状态为 – 不绘制
		}
                break;
	case kColorFill:
		if (Qt::RightButton == event->button())
		{
			points.clear();
		}
		break;
 	default:
 		break;

 	}
 	current_figure_ = NULL;
}
//鼠标绘制函数函数体
void viewwidget::paintEvent(QPaintEvent *)
{
 	QPainter  painter(this); //定义painter在this指向的控件(此例为ViewWidget)中绘图

	for (size_t i = 0; i < figure_array_.size(); i++)
	{
		painter.setPen(figure_array_[i]->color3);

		figure_array_[i]->Draw(painter);
	}
	
		if (draw_status_)
	   {
		switch (figure_type_)
		{
		case kLine:
			painter.setPen(color);
			painter.drawLine(start_point_, end_point_);
			break;

		case kRectangle:
			painter.setPen(color);
			painter.drawRect(start_point_.rx(), start_point_.ry(), end_point_.rx() - start_point_.rx(), end_point_.ry() - start_point_.ry());
			break;

		case kEllipse:
			painter.setPen(color);
			painter.drawEllipse(start_point_.rx(), start_point_.ry(), end_point_.rx() - start_point_.rx(), end_point_.ry() - start_point_.ry());
			break;

		case kPolygon:
		{
			
			painter.setPen(color);
                        painter.drawLine(start_point_, end_point_);
			 if (points.size() == 0)
				 {
					 break;
				 }
				 QPoint pre = points[0];
		 
			 for (int i = 1; i < points.size(); i++)
				 {
				 painter.drawLine(pre, points[i]);
				 pre = points[i];
				 }

		}
			break;

		case kFreehand:
		painter.setPen(color);
		if (path!=NULL)
		{
			painter.drawPath(*path);
		}
			break;

		case  kFreehand1:
		{
			painter.setPen(color);

			if (points.size() == 0)
			{
				break;
			}
			QPoint pre = points[0];

			for (int i = 1; i < points.size(); i++)
			{
			painter.drawLine(pre, points[i]);
			pre = points[i];
			}
		}
			break;
		case kColorFill:
		{
			 if (points.size() == 0)
			 {
			  break;
			 }
			 QPen pen = painter.pen();
			 painter.setPen(color);
			 pen.setColor(Qt::red);
			colorFilling(points, painter);
		}
			break;

		default:
			break;
		}
	}

	update();    //更新窗口

}

void viewwidget::colorFilling(std::vector<QPoint> ThePolygon, QPainter &paint)
{

	int minY, maxY;
	minY = maxY = (*ThePolygon.begin()).ry();
	for (int i = 0; i < ThePolygon.size(); i++)
	{
		if (ThePolygon[i].ry()<minY)
		{
			minY = ThePolygon[i].ry();
		}
		if (ThePolygon[i].ry()>maxY)
		{
			maxY = ThePolygon[i].ry();
		}
	}

	std::vector<bool> isUpDown(ThePolygon.size(), false);

	for (int i = 0; i < ThePolygon.size(); i++)
	{
		QPoint p = ThePolygon[i];
		QPoint p_pre = ThePolygon[(i == 0) ? ThePolygon.size() - 1 : i - 1];
		QPoint p_next = ThePolygon[(i == ThePolygon.size() - 1) ? 0 : i + 1];
		int tp = (p.ry() - p_pre.ry())*(p.ry() - p_next.ry());
		if (tp < 0)
		{
			isUpDown[i] = true;
		}
	}

	for (int h = minY; h <= maxY; h++)
	{
		std::vector<int> intersects;
		std::vector<bool> isVertsUsed(ThePolygon.size(), false);
		for (int j = 0; j < ThePolygon.size(); j++)
		{
			QPoint start_pt = ThePolygon[j];
			int end_idx = (j == ThePolygon.size() - 1) ? 0 : j + 1;
			QPoint end_pt = ThePolygon[end_idx];
			int res_x = calculate_x_intersect(start_pt, end_pt, h);

			if (res_x == -1)
			{
				continue;
			}

			if (res_x==-2&&start_pt.ry() == end_pt.ry())
			{
				QPoint p_pre_pre = ThePolygon[(j == 0) ? ThePolygon.size() - 1 : j - 1];
				QPoint p_next_next = ThePolygon[(j == ThePolygon.size() - 1) ? 0 : j + 1];
				if ((start_pt.ry() - p_pre_pre.ry())*(start_pt.ry()-p_next_next.ry())<0)
				{
					intersects.push_back(start_pt.rx());
				}
				continue;
			}

			if (h == start_pt.ry())
			{
				if (isUpDown[j])
				{
					if (!isVertsUsed[j])
					{
						isVertsUsed[j] = true;
						intersects.push_back(start_pt.rx());
					}
					else
					{
						continue;
					}
				}
				else
				{
					intersects.push_back(start_pt.rx());
				}
				continue;
			}


			if (h == end_pt.ry())
			{
				if (isUpDown[end_idx])
				{
					if (!isVertsUsed[end_idx])
					{
						isVertsUsed[end_idx] = true;
						intersects.push_back(end_pt.rx());
					}
					else
					{
						continue;
					}
				}

				else
				{
					intersects.push_back(end_pt.rx());
				}
				continue;
			}

			intersects.push_back(res_x);
		}

		if (intersects.size() % 2 == 1)
		{
			intersects.push_back(intersects[intersects.size()-1]);
		}

		std::sort(intersects.begin(), intersects.end());

		if (intersects.size() < 2)
		{
			continue;
		}
	
		for (int i = 0; i < intersects.size(); i = i + 2)
		{
			QPoint p(intersects[i], h);
			QPoint q(intersects[i + 1], h);
			paint.drawLine(p, q);
		}

		intersects.clear();
	}
}


int viewwidget::calculate_x_intersect(QPoint point_a, QPoint point_b, int y)
{
	float slop = 0;            //多边形的任意边的斜率
	float intercept = 0;      //多边形的任意边的直线方程中的截距
	int x_inter = 0;          //交点坐标x
	float y_1;


    if ((point_a.ry() < y&&point_b.ry() > y || point_a.ry() > y&&point_b.ry() < y) && (point_a.rx() == point_b.rx()))
	{
		x_inter = point_a.rx();    //与斜率不存在的线段相交
	}

	else if (point_a.ry() == point_b.ry())
	{
		//x_inter = point_a.rx();
		x_inter = -2;            //斜率为0,返回-2
	}

	else if (point_a.ry() == y&&point_b.ry() != y)
	{
		x_inter = point_a.rx();   //与线段的端点相交
	}

	else if (point_b.ry() == y&&point_a.ry() != y)
	{
		x_inter = point_b.rx();   //与线段的端点相交
	}

	else if (point_a.ry() > y&&point_b.ry() > y || point_a.ry() < y&&point_b.ry() < y)
	{
		x_inter = -1;           //没有交点,返回-1
	}

	else if ((point_a.ry() < y&&point_b.ry() > y || point_a.ry() > y&&point_b.ry() < y) && (point_a.ry() != point_b.ry()) && (point_a.rx() != point_b.rx()))
	{
		slop = float((point_b.rx() - point_a.rx())) / float((point_b.ry() - point_a.ry())); //计算直线方程的参数
		intercept = float(point_a.rx()) - slop*float(point_a.ry());
		x_inter = int(slop*float(y) + intercept+0.5);     //交点坐标x
	}

	return x_inter;

}


接下来是不同的绘制类型的定义,便于多次绘制:


#ifndef _FIGURES_H_
#define _FIGURES_H_
#include <QPainter>
#include <vector>
#include <QPainterPath>
#include <QColor>
#include <algorithm>
#include <iostream>

class CFigures
{
public:
	CFigures();
	virtual ~CFigures();
	QColor color3;
	virtual void Draw(QPainter &paint);

};

class Line :public CFigures
{

public:
	Line(void);
	Line(int start_point_x, int start_point_y, int end_point_x, int end_point_y)
	{
		start_point_x_ = start_point_x;
		start_point_y_ = start_point_y;
		end_point_x_ = end_point_x;
		end_point_y_ = end_point_y;
	}

	~Line()
	{
		;
	}

public:
	void Draw(QPainter &paint)
	{
		paint.drawLine(start_point_x_, start_point_y_, end_point_x_, end_point_y_);
	}
private:
	int start_point_x_, start_point_y_, end_point_x_, end_point_y_;
};

class Rectangle:public CFigures
{

public:
	Rectangle(void);
	Rectangle(int start_point_x, int start_point_y, int end_point_x, int end_point_y)
	{
		start_point_x_ = start_point_x;
		start_point_y_ = start_point_y;
		end_point_x_ = end_point_x;
		end_point_y_ = end_point_y;
	}

	~Rectangle()
	{
		;
	}

public:
	void Draw(QPainter &paint)
	{
		paint.drawRect(start_point_x_, start_point_y_, end_point_x_ - start_point_x_, end_point_y_ - start_point_y_);
	}
private:
	int start_point_x_, start_point_y_, end_point_x_, end_point_y_;
};

class Ellipse : public CFigures
{

public:
	Ellipse(void);
	Ellipse(int start_point_x, int start_point_y, int end_point_x, int end_point_y)
	{
		start_point_x_ = start_point_x;
		start_point_y_ = start_point_y;
		end_point_x_ = end_point_x;
		end_point_y_ = end_point_y;
	}

	~Ellipse()
	{
		;
	}

public:
	void Draw(QPainter &paint)
	{
		paint.drawEllipse(start_point_x_, start_point_y_, end_point_x_ - start_point_x_, end_point_y_ -start_point_y_);
	}
private:
	int start_point_x_, start_point_y_, end_point_x_, end_point_y_;
};

class Polygon : public CFigures
{

public:
	Polygon(void);
	Polygon(std::vector<QPoint>points)
	{
		point = points;
	}

	~Polygon()
	{
		;
	}

public:

	void Draw(QPainter &paint)
	{
		paint.drawPolygon(&point.at(0), point.size());
	}

private:

   std::vector<QPoint>point;
 
};

class Freehand : public CFigures
{
public:
	Freehand(void);
	Freehand(QPainterPath paths)
	{
		path = paths;
	}

	~Freehand()
	{
		;
	}

public:
	void Draw(QPainter &paint)
	{
		QLinearGradient myGradient;
		//QPen myPen;
		myGradient.setColorAt(0, Qt::black);
		paint.setBrush(myGradient);
		//paint.setPen(myPen);//以上几句话对颜色填充有作用,注释掉就不填充颜色
		paint.drawPath(path);
	}
private:
	QPainterPath path;
};

#endif

C文件如下:

#include "figures.h"
#include "viewwidget.h"
#include <QColorDialog>
CFigures::CFigures()
{
	;
}

CFigures::~CFigures()
{
	;
}

void CFigures::Draw(QPainter &paint)
{
	;
}


这次分享的代码中,其实还有很多不完善的地方。改了很久,还是有些小漏洞没改完。


以下是简单的绘图结果截屏:



以下这张图是Qt中自带的path绘制自由曲线以及自带的颜色填充结果:




以下这张图是简单的自由曲线:




以下这张图是对自己绘制的多边形进行颜色填充,图形不负责,所以填充效果不错:




以下这张图就比较差劲了,扫描线程序没有写好。就是对于一些特殊情况没有考虑到。



评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值