【问题标题】:Finding and Connecting Multiple Canvas Items in Python在 Python 中查找和连接多个 Canvas 项目
【发布时间】:2013-08-11 00:25:59
【问题描述】:



背景: 我有一个代码,它生成规则形状(在本例中为三角形)网络的​​笛卡尔坐标,然后将形状的顶点绘制在Tkinter 画布作为小圆圈。该过程是自动化的,只需要网络的高度和宽度即可获得画布输出。每个顶点都有标签“顶点”和顶点的编号。
问题: 我想自动将形状的顶点连接在一起(即点到点),我已经研究过使用 find_closestfind_overlapping 方法来做到这一点,但作为网络由相互成角度的顶点组成,我经常发现find_overlapping 不可靠(由于依赖于矩形信封),而find_closest 似乎仅限于找到一个连接。由于顶点不一定按顺序连接,因此不可能创建一个循环来简单地连接顶点 1 --> 顶点 2 等。
问题:有没有办法有效地得到所有顶点的相邻顶点,然后“连接点”而不依赖于使用手动方法(例如self.c.create_line(vertex_coord[1], vertex_coord[0], fill='black'))为每个连接单独创建点之间的线?是否可以分享一个这样的代码的小例子?

提前感谢您的帮助!

以下是我的代码画布组件的缩略版。

原型方法:

from data_generator import * 
run_coordinate_gen=data_generator.network_coordinates()
run_coordinate_gen.generator_go()

class Network_Canvas:
  def __init__(self, canvas):
        self.canvas=canvas
        canvas.focus_set()
        self.canvas.create_oval(Vertex_Position[0],  dimensions[0],   fill='black', tags=('Vertex1', Network_Tag, Vertex_Tag))
        self.canvas.create_oval(Vertex_Position[5],  dimensions[5],   fill='black', tags=('Vertex2', Network_Tag, Vertex_Tag))
        try:
            self.canvas.create_line(Line_Position[5] ,Line_Position[0] , fill='black' tags=(Network_Tag,'Line1', Line_Tag )) #Connection Between 1 and 6 (6_1), Line 1
        except:
            pass

#Note: Line_Position, Dimensions and Vertex_Position are all lists composed of (x,y) cartesian coordinates in this case.

这当然会被复制到整个网络中的每条线和顶点,但仅用于 90 个顶点。新版本需要更多数量级的顶点,我正在这样做:
新方法:

#Import updated coordinate generator and run it as before
class Network_Canvas:
    def __init__(self, canvas):
        self.canvas=canvas
        canvas.focus_set()
        for V in range(len(vertex_coord_xy)):                                                   
            self.canvas.create_text(vertex_coord_xy[V]+Text_Distance, text=V+1, fill='black', tags=(V, 'Text'), font=('Helvetica', '9'))
            self.canvas.create_oval(vertex_coord_xy[V],vertex_coord_xy[V]+Diameter, fill='black', outline='black', tags=(V, 'Vertex'))      
    #loop to fit connections here (?) 

【问题讨论】:

  • 如果你能解释为什么跟踪哪些顶点连接到哪个顶点并使用create_line 不是一个可接受的解决方案(对我来说似乎非常简单和高效),它会帮助我们想到另一个解决方案。
  • 我正在寻找一种方法来自动跟踪每个顶点的连接,而无需手动更改每条新线的坐标。由于网络可能很广泛(> 1000 个顶点),因此手动输入连接不是一种实用的方法,并且在顶点之间画线的简单循环即self.c.create_line(vertex_coord[i], vertex_coord[i+1]) 等不能正确地“连接点”。我目前的方法依赖于手动跟踪连接,但它很耗时并且对于大量顶点没有用处。希望有帮助:)
  • 另一个问题:当你写self.c.create_line(vertex_coord[0], vertex_coord[1], fill='black')时,你的意思一定是self.c.create_line(vertex0_coord[0], vertex0_coord[1], vertex1_coord[0], vertex1_coord[1], fill='black'),因为你需要4个坐标代表一条线。这就是你在实际代码中所做的吗?
  • 你说的完全正确,是的。实际代码类似于:self.c.create_line(vertex_coord_x[V],vertex_coord_y[V],vertex_coord_x[V-1],vertex_coord_y[V-1], fill='black', tags=(V, 'network', 'connection')。当然,目前这会在整个网络中以 V 升序绘制一系列非常混乱的线。

标签: python algorithm loops python-2.7 tkinter


【解决方案1】:

我认为任何类型的最近邻搜索都将比仅仅跟踪顶点更耗时,而且没有我能想到的“自动”连接点方法(另外,我不明白为什么这样的方法应该比使用 create_line 绘制它们更快)。此外,如果您不跟踪,最近邻搜索算法将如何区分两个单独的、附近(或重叠)形状的顶点?无论如何,在我看来你已经有了正确的方法;可能有一些方法可以优化它。

我认为,由于您的形状很多,而且您需要对它们做一些复杂的事情,所以我会为它们创建一个类,就像我在下面实现的那样。它包括“单击查看相邻顶点”功能。以下所有代码都运行没有错误。输出图像如下所示。

import Tkinter as TK
import tkMessageBox

# [Credit goes to @NadiaAlramli](http://stackoverflow.com/a/1625023/1460057) for the grouping code
def group(seq, groupSize):
    return zip(*(iter(seq),) * groupSize)

Network_Tag, Vertex_Tag, Line_Tag = "network", "vertex", "line"

class Shape:
    def __init__(self, canvas, vertexCoords, vertexDiam):
        self.vertexIDs = []
        self.perimeterID = None
        self.vertexCoords = vertexCoords
        self.vertexRadius = vertexDiam/2
        self.canvas = canvas

    def deleteVertices(self):
        for ID in self.vertexIDs:
            self.canvas.delete(ID)
        self.vertexIDs = []

    def bindClickToVertices(self):
        coordsGrouped = group(self.vertexCoords, 2)
        num = len(coordsGrouped)
        for k in range(len(self.vertexIDs)):
            others = [coordsGrouped[(k-1)%num], coordsGrouped[(k+1)%num]]
            self.canvas.tag_bind(self.vertexIDs[k], '<Button-1>',
                    lambda *args:tkMessageBox.showinfo("Vertex Click", "Neighboring vertices: "+str(others)))

    def drawVertices(self):
        for x, y in group(self.vertexCoords, 2):
            self.vertexIDs.append(self.canvas.create_oval(x-self.vertexRadius, y-self.vertexRadius, x+self.vertexRadius, y+self.vertexRadius, fill='black', tags=(Network_Tag, Vertex_Tag)))
        self.bindClickToVertices()

    def updateVertices(self):
        self.deleteVertices()
        self.drawVertices()

    def deletePerimeter(self):
        if self.perimeterID is not None:
            self.canvas.delete(self.perimeterID)
        self.perimeterID = None

    def drawPerimeter(self):
        print "creating line:", (self.vertexCoords + self.vertexCoords[0:2])
        self.perimeterID = self.canvas.create_line(*(self.vertexCoords + self.vertexCoords[0:2]), fill='black', tags=(Network_Tag, Line_Tag))

    def updatePerimeter(self):
        self.deletePerimeter()
        self.drawPerimeter()

    def deleteShape(self):
        self.deleteVertices()
        self.deletePerimeter()

    def updateShape(self):
        self.updateVertices()
        self.updatePerimeter()

可以很简单的使用,像这样:

root = TK.Tk()

frame = TK.Frame(root)
canvas = TK.Canvas(frame, width=1000, height=1000)
frame.grid()
canvas.grid()

# create a bunch of isoceles triangles in different places:
shapes = []
for dx, dy in zip(range(0,1000, 30), range(0,1000, 30)):
    shapes.append(Shape(canvas, [0+dx, 0+dy, 10+dx, 10+dy, 20+dx, 0+dy], 5))

# draw (or redraw) the shapes:
for shape in shapes:
    shape.updateShape()

# move one of the shapes and change it to a square
shapes[10].vertexCoords = [50, 10, 60, 10, 60, 20, 50, 20]
shapes[10].updateShape()

# delete all the odd-numbered shapes, just for fun:
for k in range(len(shapes)):
    if k%2 == 1:
        shape.deleteShape()

root.mainloop()

输出:

【讨论】:

  • 我没想到你能做到,谢谢!我还将在问题中发布示例代码 sn-p :) 关于最近邻算法,我一直在想象我需要做的事情,即在距原点已知距离 r 处有一个顶点被 n 个顶点包围,然后将一条线捕捉到距离 r 内的每个顶点。如果我可以为此编写一个循环,它将贯穿每个顶点并拟合连接。由于我的原型使用了 1000,我不确定它是否是可行的方法了 :(
  • 嗯 - 如果您的意思是您正在为每条外围线的代码输入 create_line 语句,那么我们绝对可以在您发布该 sn-p 后立即为您找到一种自动化方法。
  • 对于延迟添加 sn-ps 深表歉意,我当然很乐意提供您可能需要的任何其他代码区域!感谢您的帮助。
  • 进一步说明,我寻找最近邻信息的另一部分原因是,理想情况下,我需要能够点击一个顶点并让它给我相邻的顶点'坐标和它自己的坐标(以及其他各种信息)。当然,目前我仅限于顶点本身的坐标,而且我的旧方法通过一种不容易扩展到数百个顶点的费力方法实现了这一点。
  • 非常感谢您,我将尝试将其调整为我的代码,我会及时通知您并让您知道是否解决了我的问题。再次感谢您:)
猜你喜欢
  • 1970-01-01
  • 2019-09-19
  • 1970-01-01
  • 2011-08-03
  • 2020-07-16
  • 1970-01-01
  • 2020-09-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多