gpt4 book ai didi

python - 如何在pyqt4中向我的行添加箭头?

转载 作者:行者123 更新时间:2023-12-04 02:04:54 29 4
gpt4 key购买 nike

我得到了这个代码:

from PyQt4 import QtGui, QtCore

class MyFrame(QtGui.QGraphicsView):
def __init__( self, parent = None ):
super(MyFrame, self).__init__(parent)

scene = QtGui.QGraphicsScene()
self.setScene(scene)
self.resize( 400, 240 )

# http://pyqt.sourceforge.net/Docs/PyQt4/qpen.html
pencil = QtGui.QPen( QtCore.Qt.black, 2)
pencil.setStyle( QtCore.Qt.SolidLine )

# pencil.setStyle( QtCore.Qt.UpArrow )
scene.addLine( QtCore.QLineF( 0, 0, 100, 100 ), pencil )

if ( __name__ == '__main__' ):
app = QtGui.QApplication([])
f = MyFrame()
f.show()
app.exec_()

其中绘制此窗口:

enter image description here

如何在线条的一端添加箭头,因为我使用图像编辑器在最后一张图像上绘制:

enter image description here

我发现这个 C++ 教程 http://www.codeproject.com/Articles/3274/Drawing-Arrows用这个伪代码:
// ARROWSTRUCT
//
// Defines the attributes of an arrow.
typedef struct tARROWSTRUCT {
int nWidth; // width (in pixels) of the full base of the arrowhead
float fTheta; // angle (in radians) at the arrow tip between the two
// sides of the arrowhead
bool bFill; // flag indicating whether or not the arrowhead should be
// filled
} ARROWSTRUCT;

// ArrowTo()
//
// Draws an arrow, using the current pen and brush, from the current position
// to the passed point using the attributes defined in the ARROWSTRUCT.
void ArrowTo(HDC hDC, int x, int y, ARROWSTRUCT *pArrow);
void ArrowTo(HDC hDC, const POINT *lpTo, ARROWSTRUCT *pArrow);

Simply fill an ARROWSTRUCT with the desired attributes, make sure the current DC position is correct (MoveTo(), etc.), and call one of the two ArrowTo() functions. The size parameters (nWidth and fTheta) are defined as follows:

Technique

This goes back to high-school algebra and trigonometry. The ArrowTo() function first builds a vector of the full line. Then it calculates the points for the sides of the arrowhead based on the nWidth and fTheta attributes you pass. Badda-boom-badda-bing, you got your arrowhead.

Here's some pseudo-pseudocode:


lineVector = toPoint - fromPoint
lineLength = length of lineVector

// calculate point at base of arrowhead
tPointOnLine = nWidth / (2 * (tanf(fTheta) / 2) * lineLength);
pointOnLine = toPoint + -tPointOnLine * lineVector

// calculate left and right points of arrowhead
normalVector = (-lineVector.y, lineVector.x)
tNormal = nWidth / (2 * lineLength)
leftPoint = pointOnLine + tNormal * normalVector
rightPoint = pointOnLine + -tNormal * normalVector

此外,我还可以找到另一个问题 Drawing a polygon in PyQt但它适用于qt5。因此,在pyqt4中用多边形绘制箭头是一种更好的方法吗?

最佳答案

我遇到了同样的问题,所以经过一些工作,我想出了这个。

import math, sys
from PyQt5 import QtWidgets, QtCore, QtGui


class Path(QtWidgets.QGraphicsPathItem):
def __init__(self, source: QtCore.QPointF = None, destination: QtCore.QPointF = None, *args, **kwargs):
super(Path, self).__init__(*args, **kwargs)

self._sourcePoint = source
self._destinationPoint = destination

self._arrow_height = 5
self._arrow_width = 4

def setSource(self, point: QtCore.QPointF):
self._sourcePoint = point

def setDestination(self, point: QtCore.QPointF):
self._destinationPoint = point

def directPath(self):
path = QtGui.QPainterPath(self._sourcePoint)
path.lineTo(self._destinationPoint)
return path

def arrowCalc(self, start_point=None, end_point=None): # calculates the point where the arrow should be drawn

try:
startPoint, endPoint = start_point, end_point

if start_point is None:
startPoint = self._sourcePoint

if endPoint is None:
endPoint = self._destinationPoint

dx, dy = startPoint.x() - endPoint.x(), startPoint.y() - endPoint.y()

leng = math.sqrt(dx ** 2 + dy ** 2)
normX, normY = dx / leng, dy / leng # normalize

# perpendicular vector
perpX = -normY
perpY = normX

leftX = endPoint.x() + self._arrow_height * normX + self._arrow_width * perpX
leftY = endPoint.y() + self._arrow_height * normY + self._arrow_width * perpY

rightX = endPoint.x() + self._arrow_height * normX - self._arrow_height * perpX
rightY = endPoint.y() + self._arrow_height * normY - self._arrow_width * perpY

point2 = QtCore.QPointF(leftX, leftY)
point3 = QtCore.QPointF(rightX, rightY)

return QtGui.QPolygonF([point2, endPoint, point3])

except (ZeroDivisionError, Exception):
return None

def paint(self, painter: QtGui.QPainter, option, widget=None) -> None:

painter.setRenderHint(painter.Antialiasing)

painter.pen().setWidth(2)
painter.setBrush(QtCore.Qt.NoBrush)

path = self.directPath()
painter.drawPath(path)
self.setPath(path)

triangle_source = self.arrowCalc(path.pointAtPercent(0.1), self._sourcePoint) # change path.PointAtPercent() value to move arrow on the line

if triangle_source is not None:
painter.drawPolyline(triangle_source)


class ViewPort(QtWidgets.QGraphicsView):

def __init__(self):
super(ViewPort, self).__init__()

self.setViewportUpdateMode(self.FullViewportUpdate)

self._isdrawingPath = False
self._current_path = None

def mousePressEvent(self, event: QtGui.QMouseEvent) -> None:

if event.button() == QtCore.Qt.LeftButton:

pos = self.mapToScene(event.pos())
self._isdrawingPath = True
self._current_path = Path(source=pos, destination=pos)
self.scene().addItem(self._current_path)

return

super(ViewPort, self).mousePressEvent(event)

def mouseMoveEvent(self, event):

pos = self.mapToScene(event.pos())

if self._isdrawingPath:
self._current_path.setDestination(pos)
self.scene().update(self.sceneRect())
return

super(ViewPort, self).mouseMoveEvent(event)

def mouseReleaseEvent(self, event: QtGui.QMouseEvent) -> None:

pos = self.mapToScene(event.pos())

if self._isdrawingPath:
self._current_path.setDestination(pos)
self._isdrawingPath = False
self._current_path = None
self.scene().update(self.sceneRect())
return

super(ViewPort, self).mouseReleaseEvent(event)


def main():
app = QtWidgets.QApplication(sys.argv)

window = ViewPort()
scene = QtWidgets.QGraphicsScene()
window.setScene(scene)
window.show()

sys.exit(app.exec())


if __name__ == "__main__":
main()

此代码适用于任何类型的路径,包括贝塞尔曲线、正方形等。如果您想更改箭头位置,您应该更改 path.PointAtPercent()值介于 0-1 之间的任何位置.例如,如果您想在线条中间绘制箭头,请使用 self.arrowCalc(path.pointAtPercent(0.5), path.pointAtPercent(0.51)) .此外,当您将点数传递给 arrowCalc 时确保源点和目标点靠近。
额外:
如果要测试正方形和贝塞尔路径(用以下方法替换直接路径方法):
     def squarePath(self):
s = self._sourcePoint
d = self._destinationPoint

mid_x = s.x() + ((d.x() - s.x()) * 0.5)

path = QtGui.QPainterPath(QtCore.QPointF(s.x(), s.y()))
path.lineTo(mid_x, s.y())
path.lineTo(mid_x, d.y())
path.lineTo(d.x(), d.y())

return path

def bezierPath(self):
s = self._sourcePoint
d = self._destinationPoint

source_x, source_y = s.x(), s.y()
destination_x, destination_y = d.x(), d.y()

dist = (d.x() - s.x()) * 0.5

cpx_s = +dist
cpx_d = -dist
cpy_s = 0
cpy_d = 0

if (s.x() > d.x()) or (s.x() < d.x()):
cpx_d *= -1
cpx_s *= -1

cpy_d = (
(source_y - destination_y) / math.fabs(
(source_y - destination_y) if (source_y - destination_y) != 0 else 0.00001
)
) * 150

cpy_s = (
(destination_y - source_y) / math.fabs(
(destination_y - source_y) if (destination_y - source_y) != 0 else 0.00001
)
) * 150

path = QtGui.QPainterPath(self._sourcePoint)

path.cubicTo(destination_x + cpx_d, destination_y + cpy_d, source_x + cpx_s, source_y + cpy_s,
destination_x, destination_y)

return path
输出:
enter image description here

关于python - 如何在pyqt4中向我的行添加箭头?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44246283/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com