qt 带箭头的直线 (类似viso)

20210926

好久没关注博客园,有些评论未回应。

如果要代码,点击下面连接:

https://files-cdn.cnblogs.com/files/warmlight/NewProject.rar

编译环境是:Qt5.9.0 MinGW 32 。

这个只是一个示例,功能不完全,也不排除代码有什么bug。如果要使用到项目中,一定要多测试。

 

 

 

2020.07.13

前几天有csdn网友给我提了一个bug:在画线是paint函数会进入无线循环。

我今天修改了一下画非箭头直线的函数。具体请点击博客园的链接下载。文中再贴出修改的地方。

2020.02.27

想要代码,留邮箱吧。或者到https://download.csdn.net/download/XuePiaoFei1/12195991下载,或者https://files-cdn.cnblogs.com/files/warmlight/NewProject.rar

 

 

近来Qt开发时可能遇到这样的需求:两个(或多个)矩形,要用直线将它们连接起来,之后还要把它们保存到xml中,并且能够还原。

类似于下图:

 

 首先想到的就是Qt自带的demo:diagramscene。因为demo中有箭头方向,开始节点和结束节点,连坐标都已经有了,保存还原都可以完成。但是,demo的直线不是竖直或水平的,所以肯定要对demo进行修改。(扯淡了,demo肯定不会符合个人的需求的,无论如何都要修改的。)

刚用Qt不久,网络上也搜不到类似的问题,也许是自己找不到,只能寄希望于Qt的demo,限制了自己的思想。可是目前没有更好的办法。

我的工程有几个要点,要能够保存和还原,箭头的首尾节点肯定要确定,或者说首尾节点的坐标一定要知道。

demo中的基本元素够了,我想只对arrow类作修改,希望能达到目标。

我先把我改造后的arrow类贴出来,然后再试图分析一下。

头文件:

  1 /****************************************************************************
  2 **
  3 ** Copyright (C) 2016 The Qt Company Ltd.
  4 ** Contact: https://www.qt.io/licensing/
  5 **
  6 ** This file is part of the examples of the Qt Toolkit.
  7 **
  8 ** $QT_BEGIN_LICENSE:BSD$
  9 ** Commercial License Usage
 10 ** Licensees holding valid commercial Qt licenses may use this file in
 11 ** accordance with the commercial license agreement provided with the
 12 ** Software or, alternatively, in accordance with the terms contained in
 13 ** a written agreement between you and The Qt Company. For licensing terms
 14 ** and conditions see https://www.qt.io/terms-conditions. For further
 15 ** information use the contact form at https://www.qt.io/contact-us.
 16 **
 17 ** BSD License Usage
 18 ** Alternatively, you may use this file under the terms of the BSD license
 19 ** as follows:
 20 **
 21 ** "Redistribution and use in source and binary forms, with or without
 22 ** modification, are permitted provided that the following conditions are
 23 ** met:
 24 **   * Redistributions of source code must retain the above copyright
 25 **     notice, this list of conditions and the following disclaimer.
 26 **   * Redistributions in binary form must reproduce the above copyright
 27 **     notice, this list of conditions and the following disclaimer in
 28 **     the documentation and/or other materials provided with the
 29 **     distribution.
 30 **   * Neither the name of The Qt Company Ltd nor the names of its
 31 **     contributors may be used to endorse or promote products derived
 32 **     from this software without specific prior written permission.
 33 **
 34 **
 35 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 36 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 37 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 38 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 39 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 40 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 41 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 42 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 43 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 44 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 45 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 46 **
 47 ** $QT_END_LICENSE$
 48 **
 49 ****************************************************************************/
 50 
 51 #ifndef ARROW_H
 52 #define ARROW_H
 53 
 54 #include <QGraphicsLineItem>
 55 
 56 #include "diagramitem.h"
 57 
 58 QT_BEGIN_NAMESPACE
 59 class QGraphicsPolygonItem;
 60 class QGraphicsLineItem;
 61 class QGraphicsScene;
 62 class QRectF;
 63 class QGraphicsSceneMouseEvent;
 64 class QPainterPath;
 65 QT_END_NAMESPACE
 66 
 67 enum LineType {
 68     lineType1 = 1,  //横竖
 69     lineType2,      //竖横
 70     lineType3,      //横竖横
 71     lineType4       //竖横竖
 72 };
 73 
 74 //! [0]
 75 class Arrow : public QGraphicsLineItem
 76 {
 77 public:
 78     enum { Type = UserType + 4 };
 79 
 80     Arrow(DiagramItem *startItem, DiagramItem *endItem,
 81       QGraphicsItem *parent = 0);
 82     ~Arrow() {m_bDeleteFlag = true;}
 83 
 84     int type() const override { return Type; }
 85     QRectF boundingRect() const override;
 86     QPainterPath shape() const override;
 87     void setColor(const QColor &color) { myColor = color; }
 88     DiagramItem *startItem() const { return myStartItem; }
 89     DiagramItem *endItem() const { return myEndItem; }
 90 
 91     void setItemId(QString startItemId ,QString startEndId);
 92     void updatePosition();
 93 
 94     QString getStartId(){return startId;}
 95     QString getEndId(){return endId;}
 96 
 97     void setStartItem(DiagramItem *startItem){ myStartItem = startItem; }
 98     void setEndItem(DiagramItem *endItem){ myEndItem = endItem; }
 99 
100     void setArrowFlag(bool IsArrow){bisArrow = IsArrow;}
101     bool getArrowFlag(){return bisArrow;}
102     bool getDeleteFlage() {return m_bDeleteFlag;}
103     void DrawArrow(QPainter *painter, QPointF startPt, QPointF endPt, bool bArrow);
104     void ThreeLine(QPainter *painter, QPointF startPt, QPointF endPt);//三段线
105     void TwoLine(QPainter *painter, QPointF startPt, QPointF endPt); //两段线
106     //void SetLineStyle(bool bVhvLine);//线段的折线风格
107     //void SetLineType(int nLineType);//线段的数量
108     void SetLineType(LineType lineType);
109     int GetLineType() {return m_lineType;}
110     void DrawLine(QPainter *painter, QPointF startPt, QPointF endPt);
111 protected:
112     void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) override;
113 
114 private:
115     DiagramItem *myStartItem;
116     DiagramItem *myEndItem;
117     QColor myColor;
118     QPolygonF arrowHead;
119     QString startId;
120     QString endId;
121     bool bisArrow;
122     bool m_bDeleteFlag;//
123     LineType m_lineType;
124     //bool m_bVhvLine;//true三段线,竖横竖; false三段线 横竖横
125     //bool m_bVhLine;// true两段线 竖横; false 两段线 横竖
126     //int m_nLineType;//2-2段线,3-3段线
127 };
128 
129 inline void Arrow::setItemId(QString startItemId ,QString startEndId){
130     startId = startItemId;
131     endId = startEndId;
132 }
133 //! [0]
134 
135 #endif // ARROW_H
View Code

 

cpp文件:

  1 /****************************************************************************
  2 **
  3 ** Copyright (C) 2016 The Qt Company Ltd.
  4 ** Contact: https://www.qt.io/licensing/
  5 **
  6 ** This file is part of the examples of the Qt Toolkit.
  7 **
  8 ** $QT_BEGIN_LICENSE:BSD$
  9 ** Commercial License Usage
 10 ** Licensees holding valid commercial Qt licenses may use this file in
 11 ** accordance with the commercial license agreement provided with the
 12 ** Software or, alternatively, in accordance with the terms contained in
 13 ** a written agreement between you and The Qt Company. For licensing terms
 14 ** and conditions see https://www.qt.io/terms-conditions. For further
 15 ** information use the contact form at https://www.qt.io/contact-us.
 16 **
 17 ** BSD License Usage
 18 ** Alternatively, you may use this file under the terms of the BSD license
 19 ** as follows:
 20 **
 21 ** "Redistribution and use in source and binary forms, with or without
 22 ** modification, are permitted provided that the following conditions are
 23 ** met:
 24 **   * Redistributions of source code must retain the above copyright
 25 **     notice, this list of conditions and the following disclaimer.
 26 **   * Redistributions in binary form must reproduce the above copyright
 27 **     notice, this list of conditions and the following disclaimer in
 28 **     the documentation and/or other materials provided with the
 29 **     distribution.
 30 **   * Neither the name of The Qt Company Ltd nor the names of its
 31 **     contributors may be used to endorse or promote products derived
 32 **     from this software without specific prior written permission.
 33 **
 34 **
 35 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 36 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 37 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 38 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 39 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 40 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 41 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 42 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 43 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 44 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 45 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 46 **
 47 ** $QT_END_LICENSE$
 48 **
 49 ****************************************************************************/
 50 #include "arrow.h"
 51 #include <qmath.h>
 52 #include <QPen>
 53 #include <QPainter>
 54 #include <QDebug>
 55 
 56 //! [0]
 57 Arrow::Arrow(DiagramItem *startItem, DiagramItem *endItem, QGraphicsItem *parent)
 58     : QGraphicsLineItem(parent)
 59 {
 60     m_bDeleteFlag = false;//20191121 sdl
 61     myStartItem = startItem;
 62     myEndItem = endItem;
 63     setFlag(QGraphicsItem::ItemIsSelectable, true);
 64     myColor = Qt::black;
 65     setPen(QPen(myColor, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
 66     bisArrow = true;
 67     if(myStartItem){startId = myStartItem->getItemId();}
 68     if(myEndItem){endId = myEndItem->getItemId();}
 69     m_lineType = LineType::lineType1;
 70 }
 71 //! [0]
 72 
 73 //! [1]
 74 QRectF Arrow::boundingRect() const
 75 {
 76     qreal extra = (pen().width() + 20) / 2.0;
 77 
 78     return QRectF(line().p1(), QSizeF(line().p2().x() - line().p1().x(),
 79                                       line().p2().y() - line().p1().y()))
 80         .normalized()
 81         .adjusted(-extra, -extra, extra, extra);
 82 }
 83 //! [1]
 84 
 85 //! [2]
 86 QPainterPath Arrow::shape() const
 87 {
 88     QPainterPath path = QGraphicsLineItem::shape();
 89     path.addPolygon(arrowHead);
 90     return path;
 91 }
 92 //! [2]
 93 
 94 //! [3]
 95 void Arrow::updatePosition()
 96 {
 97     QPointF endPt = mapFromItem(myEndItem, 0, 0);
 98     QLineF line(mapFromItem(myStartItem, 0, 0), endPt);
 99     setLine(line);
100 }
101 
102 
103 //! [3]
104 
105 //! [4]
106 void Arrow::paint(QPainter *painter, const QStyleOptionGraphicsItem *,
107           QWidget *)
108 {
109     if(myStartItem == NULL || myEndItem == NULL) return;
110     if(myStartItem->GetDeleteFlag() || myEndItem->GetDeleteFlag()) return;
111     if (myStartItem->collidesWithItem(myEndItem))
112         return;
113 
114     QPointF start = myStartItem->pos();
115     QPointF end = myEndItem->pos();
116     if(qFuzzyCompare(start.x(), end.x())){ //如果在同一竖直或水平线时,直接连接两节点中心点
117         DrawArrow(painter, start, end, true);
118         return;
119     }
120     if (qFuzzyCompare(start.y(), end.y())) {
121         DrawArrow(painter, start, end, true);
122         return;
123     }
124 
125     DrawLine(painter, start, end);
126 //    if(m_nLineType == 2){
127 //        TwoLine(painter, start, end);
128 //    }
129 //    else if (m_nLineType == 3) {
130 //        ThreeLine(painter, start, end);
131 //    }
132 }
133 //! [7]
134 
135 void Arrow::DrawArrow(QPainter *painter, QPointF startPt, QPointF endPt, bool bArrow)
136 {
137     if(endPt == QPointF(0, 0)){
138         return;
139     }
140     QPen myPen = pen();
141     myPen.setColor(myColor);
142     qreal arrowSize = 10;
143     painter->setPen(myPen);
144     painter->setBrush(myColor);
145 //! [4] //! [5]
146 
147     QLineF centerLine(startPt, endPt);
148 
149     QPolygonF endPolygon;
150     endPolygon = myEndItem->polygon();
151     QPointF p1 = endPolygon.first() + endPt;
152     QPointF p2;
153     QPointF intersectPoint;
154     QLineF polyLine;
155     for (int i = 1; i < endPolygon.count(); ++i) {
156         p2 = endPolygon.at(i) + endPt;
157         polyLine = QLineF(p1, p2);
158         QLineF::IntersectType intersectType =
159             polyLine.intersect(centerLine, &intersectPoint);
160         if (intersectType == QLineF::BoundedIntersection)
161             break;
162         p1 = p2;
163     }
164 
165     setLine(QLineF(endPt, startPt/*myStartItem->pos()*/));
166 
167 //! [5] //! [6]
168     if (bArrow){
169         setLine(QLineF(intersectPoint, startPt/*myStartItem->pos()*/));//如果是箭头所在的线
170     }
171     double angle = std::atan2(-line().dy(), line().dx());
172 
173     QPointF arrowP1 = line().p1() + QPointF(sin(angle + M_PI / 3) * arrowSize,
174                                     cos(angle + M_PI / 3) * arrowSize);
175     QPointF arrowP2 = line().p1() + QPointF(sin(angle + M_PI - M_PI / 3) * arrowSize,
176                                     cos(angle + M_PI - M_PI / 3) * arrowSize);
177 
178     arrowHead.clear();
179     arrowHead << line().p1() << arrowP1 << arrowP2;
180 //! [6] //! [7]
181     painter->drawLine(line());
182     //是否画箭头
183     if(bArrow){
184         painter->drawPolygon(arrowHead);
185     }
186     if (isSelected()) {
187         painter->setPen(QPen(myColor, 1, Qt::DashLine));
188         QLineF myLine = line();
189         myLine.translate(0, 4.0);
190         painter->drawLine(myLine);
191         myLine.translate(0,-8.0);
192         painter->drawLine(myLine);
193     }
194 }
195 
196 void Arrow::ThreeLine(QPainter *painter, QPointF start, QPointF end)
197 {
198     if(m_lineType == LineType::lineType4){
199         qreal midY = (start.y() + end.y()) / 2;
200         QPointF startPt = myStartItem->pos();
201         QPointF endPt = myEndItem->pos();
202 
203         endPt.setX(startPt.x());
204         endPt.setY(midY);
205         DrawArrow(painter, startPt, endPt, false);
206 
207         startPt = endPt;
208         endPt.setX(myEndItem->pos().x());
209         endPt.setY(startPt.y());
210         DrawArrow(painter, startPt, endPt, false);
211 
212         startPt = endPt;
213         endPt = myEndItem->pos();
214 
215         DrawArrow(painter, startPt, endPt, true);
216     }
217     else if(m_lineType == LineType::lineType3){
218         qreal midX = (start.x() + end.x()) / 2;
219         QPointF startPt = myStartItem->pos();
220         QPointF endPt = myEndItem->pos();
221 
222         endPt.setX(midX);
223         endPt.setY(startPt.y());
224         DrawArrow(painter, startPt, endPt, false);
225 
226         startPt = endPt;
227         endPt.setX(startPt.x());
228         endPt.setY(myEndItem->pos().y());
229         DrawArrow(painter, startPt, endPt, false);
230 
231         startPt = endPt;
232         endPt = myEndItem->pos();
233         DrawArrow(painter, startPt, endPt, true);
234     }
235 }
236 
237 void Arrow::TwoLine(QPainter *painter, QPointF startPt, QPointF endPt)
238 {
239     if(m_lineType == LineType::lineType2){
240         endPt.setX(startPt.x());
241         endPt.setY(endPt.y());
242         DrawArrow(painter, startPt, endPt, false);
243 
244         startPt = endPt;
245         endPt = myEndItem->pos();
246         DrawArrow(painter, startPt, endPt, true);
247     }
248     else if(m_lineType == LineType::lineType1){
249         endPt.setY(startPt.y());
250         DrawArrow(painter, startPt, endPt, false);
251 
252         startPt = endPt;
253         endPt = myEndItem->pos();
254         DrawArrow(painter, startPt, endPt, true);
255     }
256 }
257 
258 //void Arrow::SetLineStyle(bool bVhvLine)
259 //{
260 //    m_bVhvLine = bVhvLine;
261 //}
262 
263 void Arrow::SetLineType(LineType lineType)
264 {
265     if(lineType > lineType4 || lineType < lineType1)
266         m_lineType = lineType1;
267     else{
268         m_lineType = lineType;
269     }
270 }
271 
272 void Arrow::DrawLine(QPainter *painter, QPointF start, QPointF end)
273 {
274     if(m_lineType == LineType::lineType4){
275         qreal midY = (start.y() + end.y()) / 2;
276         QPointF startPt = myStartItem->pos();
277         QPointF endPt = myEndItem->pos();
278 
279         endPt.setX(startPt.x());
280         endPt.setY(midY);
281         DrawArrow(painter, startPt, endPt, false);
282 
283         startPt = endPt;
284         endPt.setX(myEndItem->pos().x());
285         endPt.setY(startPt.y());
286         DrawArrow(painter, startPt, endPt, false);
287 
288         startPt = endPt;
289         endPt = myEndItem->pos();
290 
291         DrawArrow(painter, startPt, endPt, true);
292     }
293     else if(m_lineType == LineType::lineType3){
294         qreal midX = (start.x() + end.x()) / 2;
295         QPointF startPt = myStartItem->pos();
296         QPointF endPt = myEndItem->pos();
297 
298         endPt.setX(midX);
299         endPt.setY(startPt.y());
300         DrawArrow(painter, startPt, endPt, false);
301 
302         startPt = endPt;
303         endPt.setX(startPt.x());
304         endPt.setY(myEndItem->pos().y());
305         DrawArrow(painter, startPt, endPt, false);
306 
307         startPt = endPt;
308         endPt = myEndItem->pos();
309         DrawArrow(painter, startPt, endPt, true);
310     }
311     else if(m_lineType == LineType::lineType2){
312         end.setX(start.x());
313         end.setY(end.y());
314         DrawArrow(painter, start, end, false);
315 
316         start = end;
317         end = myEndItem->pos();
318         DrawArrow(painter, start, end, true);
319     }
320     else if(m_lineType == LineType::lineType1){
321         end.setY(start.y());
322         DrawArrow(painter, start, end, false);
323 
324         start = end;
325         end = myEndItem->pos();
326         DrawArrow(painter, start, end, true);
327     }
328     else {//默认是linetype1
329         m_lineType = LineType::lineType1;
330         end.setY(start.y());
331         DrawArrow(painter, start, end, false);
332 
333         start = end;
334         end = myEndItem->pos();
335         DrawArrow(painter, start, end, true);
336     }
337 }
View Code

2020.07.13-------------

为了防止paint进入死循环,修改了DrawArrow函数,另外改了选中线时的操作。

 1 void Arrow::DrawArrow(QPainter *painter, QPointF startPt, QPointF endPt, bool bArrow)
 2 {
 3     if(endPt == QPointF(0, 0)){
 4         return;
 5     }
 6     QPen myPen = pen();
 7     myPen.setColor(myColor);
 8     qreal arrowSize = 10;
 9     painter->setPen(myPen);
10     painter->setBrush(myColor);
11 
12     QLineF centerLine(startPt, endPt);
13     QPolygonF endPolygon;
14     endPolygon = myEndItem->polygon();
15     QPointF p1 = endPolygon.first() + endPt;
16     QPointF p2;
17     QPointF intersectPoint;
18     QLineF polyLine;
19     for (int i = 1; i < endPolygon.count(); ++i) {
20         p2 = endPolygon.at(i) + endPt;
21         polyLine = QLineF(p1, p2);
22         QLineF::IntersectType intersectType =
23             polyLine.intersect(centerLine, &intersectPoint);
24         if (intersectType == QLineF::BoundedIntersection)
25             break;
26         p1 = p2;
27     }
28 
29     qDebug() << "drawArrow:" << m_nCountDrawArrow;
30     m_nCountDrawArrow++;
31 
32     if (bArrow){
33         setLine(QLineF(intersectPoint, startPt/*myStartItem->pos()*/));//如果是箭头所在的线
34         double angle = std::atan2(-line().dy(), line().dx());
35 
36         QPointF arrowP1 = line().p1() + QPointF(sin(angle + M_PI / 3) * arrowSize,
37                                         cos(angle + M_PI / 3) * arrowSize);
38         QPointF arrowP2 = line().p1() + QPointF(sin(angle + M_PI - M_PI / 3) * arrowSize,
39                                         cos(angle + M_PI - M_PI / 3) * arrowSize);
40 
41         arrowHead.clear();
42         arrowHead << line().p1() << arrowP1 << arrowP2;
43 
44         painter->drawLine(line());
45     }
46     else {
47         painter->drawLine(endPt, startPt);
48 //        setLine(QLineF(endPt, startPt/*myStartItem->pos()*/));
49     }
50 
51     //是否画箭头
52     if(bArrow){
53         painter->drawPolygon(arrowHead);
54     }
55     if (isSelected()) {
56         painter->setPen(QPen(myColor, 1, Qt::DashLine));
57         QLineF myLine = QLineF(startPt, endPt);
58         myLine.translate(0, 4.0);
59         painter->drawLine(myLine);
60         myLine.translate(0,-8.0);
61         painter->drawLine(myLine);
62     }
63 }
View Code

 

2020.07.13------------

这个类中,有一些代码冗余。

我在这里分了四类画线方式,即头文件中的LineType。在横竖横和竖横竖的类型中,中间线段取的是中点画线。箭头首尾的节点都有了,保存也可以的。也可以根据个人情况修改箭头类。

操作:编辑图形时,基本图形时拖拽到场景中的。连线不是拖拽,是单击选中一种类型,在场景中连接两个基本图形即可。选中一个画线方式,再次拖拽基本图形或右键鼠标后,需要再次选择画线方式,才能再次画线。右键鼠标会出现右键菜单,打开、保存、删除。保存,保存场景中图形为xml;打开,打开保存的xml文件;删除,可删除基本图形或者箭头。注意:删除基本图形时,如果基本图形有连线,连线也会被删除。

还有一些功能待完善,如修改基本图形的名称等。

结果图:

 

 

 

 稍后把代码传到csdn。如果博客园能上传代码就好了。

posted @ 2020-02-27 16:57  阳光下的小土豆  阅读(6409)  评论(6编辑  收藏  举报