Examples official
First look at the official documentation Rectangle tool in the source code:
class RectangleMapTool(QgsMapToolEmitPoint):
def __init__(self, canvas):
self.canvas = canvas
QgsMapToolEmitPoint.__init__(self, self.canvas)
self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry)
self.rubberBand.setColor(QColor(255, 0, 0, 100))
self.rubberBand.setWidth(1)
self.reset()
def reset(self):
self.startPoint = self.endPoint = None
self.isEmittingPoint = False
self.rubberBand.reset(True)
def canvasPressEvent(self, e):
self.startPoint = self.toMapCoordinates(e.pos())
self.endPoint = self.startPoint
self.isEmittingPoint = True
self.showRect(self.startPoint, self.endPoint)
def canvasReleaseEvent(self, e):
self.isEmittingPoint = False
r = self.rectangle()
if r is not None:
print("Rectangle:", r.xMinimum(), r.yMinimum(), r.xMaximum(), r.yMaximum())
def canvasMoveEvent(self, e):
if not self.isEmittingPoint:
return
self.endPoint = self.toMapCoordinates(e.pos())
self.showRect(self.startPoint, self.endPoint)
def showRect(self, startPoint, endPoint):
self.rubberBand.reset(QgsWkbTypes.PolygonGeometry)
if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y():
return
point1 = QgsPointXY(startPoint.x(), startPoint.y())
point2 = QgsPointXY(startPoint.x(), endPoint.y())
point3 = QgsPointXY(endPoint.x(), endPoint.y())
point4 = QgsPointXY(endPoint.x(), startPoint.y())
self.rubberBand.addPoint(point1, False)
self.rubberBand.addPoint(point2, False)
self.rubberBand.addPoint(point3, False)
self.rubberBand.addPoint(point4, True) # true to update canvas
self.rubberBand.show()
def rectangle(self):
if self.startPoint is None or self.endPoint is None:
return None
elif self.startPoint.x() == self.endPoint.x() or self.startPoint.y() == self.endPoint.y():
return None
return QgsRectangle(self.startPoint, self.endPoint)
def deactivate(self):
super(RectangleMapTool, self).deactivate()
self.deactivated.emit()
self.reset()
QGIS2 some grammar or wording of the document, such as QGis.Polygon in QGIS3 should QgsWkbTypes.PolygonGeometry, QgsPoint correspond QgsPointXY in QGIS3 in here I've made changes.
Source resolve
- First class inherits QgsMapToolEmitPoint
- 重写canvasPressEvent、canvasReleaseEvent、canvasMoveEvent
- Use QgsRubberBand drawn graphics rendering (support PolygonGeometry, PointGeometry, LineGeometry)
DETAILED idea is pressed when the mouse canvas trigger canvasPressEvent, to obtain a first point self.startPoint, drag the mouse to trigger canvasMoveEvent, to give the second point, the use of these two trigger points canvasReleaseEvent draw a rectangle, the mouse is released, the end draw.
Note: every time to clear before drawing graphics before, there would smear, using rubberBand the reset method:
self.rubberBand.reset(QgsWkbTypes.PolygonGeometry)
Achieve PolyDraw
QGIS editing tools built vector drawing tool has a polygonal, as follows:
Next we add a function to automatically drawn perpendicularly, source code is as follows:
class PolygonMapTool(QgsMapToolEmitPoint):
def __init__(self, canvas):
self.canvas = canvas
QgsMapToolEmitPoint.__init__(self, self.canvas)
self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry)
self.rubberBand.setColor(QColor(255, 0, 0, 100))
self.rubberBand.setWidth(1)
self.reset()
def reset(self):
self.is_start = False # 开始绘图
self.is_vertical = False # 垂直画线
self.cursor_point = None
self.points = []
self.rubberBand.reset(True)
def canvasPressEvent(self, event):
if event.button() == Qt.LeftButton:
self.points.append(self.cursor_point)
self.is_start = True
elif event.button() == Qt.RightButton:
# 右键结束绘制
if self.is_start:
self.is_start = False
self.cursor_point = None
self.show_polygon()
self.points = []
else:
pass
else:
pass
def canvasMoveEvent(self, event):
self.cursor_point = event.mapPoint()
if not self.is_start:
return
self.show_polygon()
def show_polygon(self):
self.rubberBand.reset(QgsWkbTypes.PolygonGeometry) # 防止拖影
first_point = self.points[0]
last_point = self.points[-1]
self.rubberBand.addPoint(first_point, False)
for point in self.points[1:-1]:
self.rubberBand.addPoint(point, False)
if self.cursor_point:
self.rubberBand.addPoint(QgsPointXY(last_point.x(), last_point.y()), False)
else:
self.rubberBand.addPoint(QgsPointXY(last_point.x(), last_point.y()), True)
self.rubberBand.show()
return
# 光标所在点
if self.is_vertical and len(self.points) >= 2:
countdown_second_point = self.points[-2]
cursor_point_x = self.cursor_point.x()
cursor_point_y = self.cursor_point.y()
diff_x = int(math.fabs(last_point.x() - countdown_second_point.x()))
diff_y = int(math.fabs(last_point.y() - countdown_second_point.y()))
if diff_x > diff_y:
# 最后一条线的x,y差值比较
cursor_point_x = equation(countdown_second_point.x(), countdown_second_point.y(), last_point.x(),
last_point.y(), self.cursor_point.y(), solve_type='x')
else:
cursor_point_y = equation(countdown_second_point.x(), countdown_second_point.y(), last_point.x(),
last_point.y(), self.cursor_point.x(), solve_type='y')
_cursor_point = QgsPointXY(cursor_point_x, cursor_point_y)
self.cursor_point = _cursor_point
self.rubberBand.addPoint(self.cursor_point, True)
self.rubberBand.show()
def deactivate(self):
super(PolygonMapTool, self).deactivate()
self.deactivated.emit()
self.reset()
Where equation algorithm is own implementation of the principle of a right triangle is equal to the hypotenuse of a right angle and square edges square ( ), self-understanding, to sympy library (for the first time, a good strong ...), the source code is as follows:
def equation(x1, y1, x2, y2, f, solve_type='x'):
'''
已知两点坐标和第三个点x或y,求对应的y或x
:param x1:第一个点x坐标
:param y1:第一个点y坐标
:param x2:第二个点x坐标
:param y2:第二个点y坐标
:param f:x或y的坐标值
:param solve_type:'x'或'y',默认'x'
:return:
'''
if solve_type == 'x':
x = symbols('x')
y = f
s = solve((x - x1) ** 2 + (y - y1) ** 2 - (x2 - x1) ** 2 - (y2 - y1) ** 2 - (x - x2) ** 2 - (y - y2) ** 2, x)
return s[0]
else:
y = symbols('y')
x = f
s = solve((x - x1) ** 2 + (y - y1) ** 2 - (x2 - x1) ** 2 - (y2 - y1) ** 2 - (x - x2) ** 2 - (y - y2) ** 2, y)
return s[0]