在Python中查找和连接多个画布项

时间:2013-08-11 00:25:59

标签: python algorithm loops python-2.7 tkinter



背景:我有一个代码,用于生成规则形状网络(在本例中为三角形)的笛卡尔坐标,然后将Tkinter Canvas上形状的顶点绘制为小圆圈。该过程是自动化的,只需要网络的高度和宽度即可获得画布输出。每个顶点都有标签'Vertex'和顶点的数字。
问题:我想自动将形状的顶点连接在一起(即点到点),我已经研究过使用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 (?) 

1 个答案:

答案 0 :(得分:0)

我认为任何类型的最近邻搜索都会比仅仅跟踪顶点更加耗费时间,并且没有“我能想到的”自动“连接点”方法(另外,我不明白为什么这样的方法应该比用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()

输出:

Output of demo code