我试图创建一个程序,允许用户通过使用Pyqt5绘制简单的线条来绘制不同类型的形状。
程序必须检测形状是否闭合,然后自动用定义的颜色(例如本代码中的红色)填充形状内部的区域
**问题:**该程序只理解封闭的形状,如果线绘制在一个特定的顺序,如果有人可以帮助我使这个功能的工作,而不管顺序,其中的线绘制?
我将在下面包含一个示例,以便您沿着代码更好地理解我的意思。tnx
的数据
的
import sys
from PyQt5.QtWidgets import QApplication, QGraphicsView, QGraphicsScene, QGraphicsLineItem, QMenu
from PyQt5.QtCore import Qt, QPointF
from PyQt5.QtGui import QPainter, QPen, QBrush, QColor , QPolygonF
class UserLineItem(QGraphicsLineItem):
pass
class MeasuredGraphicsView(QGraphicsView):
def __init__(self):
super().__init__()
self.scene = QGraphicsScene(self)
self.setScene(self.scene)
self.line_start = None
self.current_line = None
self.current_shape = []
self.draw_background_grid()
self.setRenderHint(QPainter.Antialiasing, True)
self.setRenderHint(QPainter.SmoothPixmapTransform, True)
self.setRenderHint(QPainter.HighQualityAntialiasing, True)
self.setRenderHint(QPainter.TextAntialiasing, True)
self.setRenderHint(QPainter.SmoothPixmapTransform, True)
self.resize(800, 600)
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self.show_context_menu)
def draw_background_grid(self):
dotted_line_pen = QPen(Qt.lightGray, 1, Qt.DotLine)
self.scene.addLine(-500, 0, 500, 0, dotted_line_pen)
self.scene.addLine(0, -500, 0, 500, dotted_line_pen)
grid_spacing = 25
for i in range(-500 + grid_spacing, 501 - grid_spacing, grid_spacing):
self.scene.addLine(i, -500, i, 500, dotted_line_pen)
self.scene.addLine(-500, i, 500, i, dotted_line_pen)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
item = self.itemAt(event.pos())
if isinstance(item, UserLineItem):
pass
else:
self.line_start = self.snap_to_grid(self.mapToScene(event.pos()))
if self.line_start.x() < -500 or self.line_start.x() > 500 or \
self.line_start.y() < -500 or self.line_start.y() > 500:
return
self.current_line = UserLineItem()
self.current_line.setPen(QPen(Qt.black, 5)) # Set pen width
self.scene.addItem(self.current_line)
self.current_shape.append(self.line_start)
elif event.button() == Qt.RightButton:
# Only show context menu if right-clicked on a UserLineItem
item = self.itemAt(event.pos())
if isinstance(item, UserLineItem):
self.show_context_menu(event.pos())
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
if self.line_start and self.current_line:
end_point = self.snap_to_grid(self.mapToScene(event.pos()))
if end_point.x() < -500 or end_point.x() > 500 or \
end_point.y() < -500 or end_point.y() > 500:
return
self.current_line.setLine(self.line_start.x(), self.line_start.y(),
end_point.x(), end_point.y())
super().mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton and self.line_start:
if self.current_line and self.line_start != self.current_shape[0]:
end_point = self.snap_to_grid(self.mapToScene(event.pos()))
if end_point.x() < -500 or end_point.x() > 500 or \
end_point.y() < -500 or end_point.y() > 500:
return
self.current_shape.append(end_point)
if len(self.current_shape) > 2 and self.current_shape[0] == self.current_shape[-1]:
self.fill_shape()
self.current_shape = [] # Reset the current shape
self.line_start = None
self.current_line = None
super().mouseReleaseEvent(event)
def snap_to_grid(self, point):
grid_spacing = 25
x = round(point.x() / grid_spacing) * grid_spacing
y = round(point.y() / grid_spacing) * grid_spacing
return QPointF(x, y)
def fill_shape(self):
poly = self.scene.addPolygon(QPolygonF(self.current_shape), QPen(Qt.black), QBrush(Qt.red))
def show_context_menu(self, pos):
item = self.itemAt(pos)
if isinstance(item, UserLineItem):
global_pos = self.mapToGlobal(pos)
context_menu = QMenu(self)
delete_action = context_menu.addAction('Delete')
action = context_menu.exec_(global_pos)
if action == delete_action:
self.scene.removeItem(item)
if __name__ == '__main__':
app = QApplication(sys.argv)
view = MeasuredGraphicsView()
view.show()
sys.exit(app.exec_())
字符串
1条答案
按热度按时间2ledvvac1#
在你的实现中有许多逻辑问题,但最重要的是这里:
字符串
上面只是检查是否至少有3个点,以及最后添加的点是否与第一个点相同。在这些简单的条件下,以下情况将在一定程度上失败:
fill_shape()
,或者创建不同的形状;fill_shape()
将以无效的点顺序调用;为了创建有效的“标准”多边形(请参见闭合多边形链),您需要遵守以下事项:
P1
到P2
)来重构它们的顺序,其中每个端点仅与一个其他矢量的起点冲突;最后一个方面是基本的:给定顶点数
n
,多边形的可能数量是(n-1)!/2
(!
是factorial:4! = 24
,如4! == 4 × (4 - 1) × (4 - 2)
等)。4个点可以产生3个不同的迭代/多边形,5个点产生12个多边形,依此类推。例如,给定4个任意点,可以得到以下结果:
x1c 0d1x的数据
现在,让我们按顺序进入正题(双关语)。
您可能对创建闭合面感兴趣(不管边数)就像你在纸上画它一样:你在脑海中想象它,然后画出它的线,即使它们不是按照最终多边形的顺序。为了实现这一点,你必须创建一个点的“Map”,并最终在所有点都成为实际顶点时创建闭合多边形。(也就是:每个点都是一个段的开始和结束)。
实现这一点的一种可能性是创建一个字典,它使用顶点作为键,行项作为值。每个真实的顶点正好对应两个行项,只要所有顶点都正好有两个行项,你就有一个封闭的多边形。
一旦一个新的段被完成,我们尝试用以下过程创建一个连续的路径:
1.取线段的两个点中的一个作为参考(不管是哪一个:只要一个顶点只对应于一个或两个向量,另一个点最终会到达闭合路径);
1.检查点是否至少对应于2条线:如果不是,则没有闭合多边形,因此我们退出整个过程;
1.得到线(段),排除当前的一个,并检查不是当前顶点的另一段的点;
1.如果另一个段的另一个点是引用(在
1.
中使用),则路径关闭;否则,从2重新开始;下面的代码是你的代码的修改版本,实现了上面的内容。注意:
x()
和y()
值代替;UserLineItem
子类中添加了一些辅助函数,这样它就可以直接访问QLineF
的getter和setter函数;setSceneRect()
来正确设置场景边界;CustomContextMenu
策略没有太大意义,除非外部原因确实需要;我用contextMenuEvent()
替代了它;型