如何将一条线的点移动到特定位置?

时间:2018-12-25 17:47:11

标签: python tkinter line

我正在尝试使用tkinter可视化python中的正弦波加法,并且试图在每个圆心之间构建线,但是到目前为止,我尝试过的工作并没有达到我的预期。是否可以解决我尝试过的问题(请参见代码),或者仅使一条线的一个坐标点独立于另一条坐标线移动?

如您在代码中看到的那样,如果您运行它,我就尝试了一种方法,其中每次迭代都删除前一行,并创建一个新的方法。当我运行代码时,实际上就像我想要的那样,在圆的每个中心之间都有一条线,但事实是这些线持续存在并且不会擦除自身。由于某些原因,canvas.delete(line)似乎没有按我预期的那样工作。

这是完整的代码。有趣的部分是在“ updateline”功能中,在“ act()”函数中。

import math
import tkinter as tk

##important to know! -- the way I'm creating the circles is by setting an object, the bounds of the circle, depending on amplitude asked by user.
##then the programs calculates the path of these bounds, depending on circles, amplitude, phase and frequency of the sine waves asked by the user from the tkinter GUI.
##finally, the program creates and moves along this path a circle, representing visually the sine wave.

top = tk.Tk()
top.title('Superposition')
choice = tk.Tk()
choice.title('Parametres')

f = tk.Frame(choice,bd=3)
f.pack(side='top')

g = tk.Frame(choice,bd=3)
g.pack(side='bottom')

tk.Label(f,text="nbre ondes:",width = 10).grid(row=0,column=0)
sines = tk.Spinbox(f,from_=1,to=50,width=10,textvariable=tk.DoubleVar(value=2))
sines.grid(row=0,column=1)
sines.delete(0,5)
sines.insert(0,2)

delai = tk.Scale(g, orient='vertical', from_=100, to=1,resolution=1, length=100,label='delai')
delai.grid(row=0,column=0)
hauteur = tk.Scale(g, orient='vertical', from_=1100, to=100,resolution=100, length=100,label='fenetre')
hauteur.grid(row=0,column=1)
taillec1 = tk.Scale(g, orient='vertical', from_=3.5, to=0.1,resolution=0.1, length=100,label='taille')
taillec1.grid(row=0,column=2)
delai.set(20)
hauteur.set(600)
taillec1.set(1.5)

def grilledechoix():
    numberofsines = int(sines.get())
    for i in f.grid_slaves():
        if int(i.grid_info()["row"]) > numberofsines+2:
            i.grid_forget()

    for i in range(1,numberofsines+1):
        tk.Label(f,text="phase n."+str(i),width = 10).grid(row=i+2,column=4)
        phase = tk.Spinbox(f,from_=-180,to=180,width=10)
        phase.grid(row=i+2,column=5)
        phase.delete(0,5)
        phase.insert(0, 0)
    for i in range(1,numberofsines+1):
        tk.Label(f,text="amp. n."+str(i),width = 10).grid(row=i+2,column=0)
        ampli = tk.Spinbox(f,from_=1,to=10000000,width=10)
        ampli.grid(row=i+2,column=1)
        ampli.delete(0,5)
        ampli.insert(0,10)
    for i in range(1,numberofsines+1):
        tk.Label(f,text="freq n."+str(i),width = 10).grid(row=i+2,column=2)
        freq = tk.Spinbox(f,from_=-1000,to=1000,width=10)
        freq.grid(row=i+2,column=3)
        freq.delete(0,5)
        freq.insert(0,5)


def act():        
    h = g.grid_slaves()[1].get()
    delai = g.grid_slaves()[2].get()
    taillec1 = g.grid_slaves()[0].get()
    w = h

    ampdict = {'box1':100 * ((h/700)*taillec1)}
    frqdict = {}
    aaadict = {}
    fffdict = {}
    phadict = {}

    numberofsines = int(sines.get())

    sin = lambda degs: math.sin(math.radians(degs))
    cos = lambda degs: math.cos(math.radians(degs))

    for i in range(1,numberofsines+1):
        fffdict['box'+str(numberofsines-i+1)] = f.grid_slaves()[(2*i)-2].get()
        aaadict['box'+str(numberofsines-i+1)] = f.grid_slaves()[(2*i)-2+2*numberofsines].get()
        phadict['box'+str(numberofsines-i+1)] = f.grid_slaves()[(2*i)-2+4*numberofsines].get()
    for i in range(1,numberofsines+1):
        ampdict['box'+str(i)] = (float(ampdict['box1'])/float(aaadict['box1'])) * float(aaadict['box'+str(i)])
        frqdict['box'+str(i)] = float(fffdict['box'+str(i)])/float(fffdict['box1'])

    class obj(object):
        cos0, cos180 = cos(0), cos(180)
        sin90, sin270 = sin(90), sin(270)

        def __init__(i, x, y, rayon):
            i.x, i.y = x, y
            i.rayon = rayon

        def bounds(i):
            return (i.x + i.rayon*i.cos0,   i.y + i.rayon*i.sin270,
                    i.x + i.rayon*i.cos180, i.y + i.rayon*i.sin90)

    def updateposition(canvas, id, cent, obj, path):
        obj.x, obj.y = next(path)
        x0, y0, x1, y1 = canvas.coords(id)
        oldx, oldy = (x0+x1) // 2, (y0+y1) // 2
        dx, dy = obj.x - oldx, obj.y - oldy
        canvas.move(id, dx, dy)
        canvas.move(cent, dx, dy)
        canvas.after(delai, updateposition, canvas, id, cent, obj, path)

    def updateline(canvas, line, robj0, cent0, robj1, cent1):
        x00, y00, x01, y01 = canvas.coords(cent0)  ##defining coords of the two ovals asked, representing centers of circles
        x10, y10, x11, y11 = canvas.coords(cent1)
        oldx0, oldy0 = (x00+x01) // 2, (y00+y01) // 2 ##defining center coords of the two ovals
        oldx1, oldy1 = (x10+x11) // 2, (y10+y11) // 2
        dx0, dy0 = robj0.x - oldx0, robj0.y - oldy0 ##defining the deltax and deltay, difference of movements between frames, of the two ovals
        dx1, dy1 = robj1.x - oldx1, robj1.y - oldy1
        canvas.after(delai, canvas.delete, line) ##deleting previous line, does not work and I don't know why. I've also tried 'canvas.delete(line)', giving same results
        canvas.create_line(oldx0+dx0, oldy0+dy0, oldx1+dx1, oldy1+dy1) ##creating new line
        canvas.after(delai, updateline, canvas, line, robj0, cent0, robj1, cent1) ##function invoking itself after delay 'delai'

    def posobj(pt,ang,deltang):
        while True:
            yield pt.x + pt.rayon*cos(ang), pt.y + pt.rayon*sin(ang)
            ang = (ang+deltang)%360

    try:
        top.pack_slaves()[0].destroy()
    except:
        pass

    canvas = tk.Canvas(top, bg='white', height=h, width=w)
    canvas.pack(side='right')

    robj = {}
    r = {}
    posobjet = {}
    line = {}
    cent = {}

## the following 'for' loop creates a number of circles corresponding to sine waves, as much as the user asked.
    for i in range(1,int(sines.get())+2):
        if i != int(sines.get())+1:
            if i == 1:
                robj[str(i)] = obj(h/2,h/2,float(ampdict['box'+str(i)]))
                r[str(i)] = canvas.create_oval(robj[str(i)].bounds(),fill='',outline='black')
                cent[str(i)] = canvas.create_oval(h/2+h/200,h/2+h/200.,h/2-h/200,h/2-h/200, fill='white', outline='red')
                posobjet[str(i)] = posobj(robj[str(i)],float(phadict['box'+str(i)]),float(frqdict['box'+str(i)]))
            else:
                robj[str(i)] = obj(robj[str(i-1)].x,robj[str(i-1)].y,float(ampdict['box'+str(i)]))
                r[str(i)] = canvas.create_oval(robj[str(i)].bounds(),fill='',outline='black')
                cent[str(i)] = canvas.create_oval(robj[str(i)].x+h/200,robj[str(i)].y+h/200,robj[str(i)].x-h/200,robj[str(i)].y-h/200, fill='white', outline='blue')
                line[str(i)] = canvas.create_line(0,0,0,0)
                posobjet[str(i)] = posobj(robj[str(i)],float(phadict['box'+str(i)]),float(frqdict['box'+str(i)]))
                top.after(delai, updateposition, canvas, r[str(i)], cent[str(i)], robj[str(i)], posobjet[str(i-1)])
                ##here I'm invoking the updateline function using the constant 'delai', the line i, and objects defining the bounds of the center objects, the little blue/red dots appearing as the center of each circles(run the code, it'll be easier to understand)
                top.after(delai, updateline, canvas, line[str(i)], robj[str(i-1)], cent[str(i-1)], robj[str(i)], cent[str(i)])
        else:
            robj[str(i)] = obj(robj[str(i-1)].x,robj[str(i-1)].y,h/200)
            r[str(i)] = canvas.create_oval(robj[str(i)].bounds(),fill='white',outline='red')
            cent[str(i)] = canvas.create_oval(robj[str(i)].x+h/200,robj[str(i)].y+h/200,robj[str(i)].x-h/200,robj[str(i)].y-h/200, fill='white', outline='red')
            line[str(i)] = canvas.create_line(0,0,0,0)
            top.after(delai, updateposition, canvas, r[str(i)], cent[str(i)], robj[str(i)], posobjet[str(i-1)])
            ##2nd and last time invoking the updateline function, for the line between the last circle's point and the final red point.
            top.after(delai, updateline, canvas, line[str(i)], robj[str(i-1)], cent[str(i-1)], robj[str(i)], cent[str(i)])
    top.mainloop()

ok = tk.Button(f,text='NBRE',command=grilledechoix)
ok.grid(row=0,column=2)
ac = tk.Button(f,text='APPLY',command=act)
ac.grid(row=0,column=3)
grilledechoix()
act()

我希望一旦updateline函数再次调用自身,这些行就会消失,因为更新行中有'canvas.delete(line)'行,我真不明白为什么这样做。 无论如何,如果您有解决方案来使行移动,而不必在每次调用函数时都创建和删除它们,请随时告诉我。

谢谢!

1 个答案:

答案 0 :(得分:0)

如果我正确理解了问题,则认为问题出在以下代码上:

canvas.after(delai, canvas.delete, line)
canvas.create_line(oldx0+dx0, oldy0+dy0, oldx1+dx1, oldy1+dy1)
canvas.after(delai, updateline, canvas, line, robj0, cent0, robj1, cent1)

它无法将 new 行重新分配给下一次调用的line变量。而是尝试:

canvas.after(delai, canvas.delete, line)
line = canvas.create_line(oldx0+dx0, oldy0+dy0, oldx1+dx1, oldy1+dy1)
canvas.after(delai, updateline, canvas, line, robj0, cent0, robj1, cent1)

我运行它时除去了多余的行。让我知道是否错过了重点。