I have the following bits of code that creates a toplevel window and parses a dictionary into a Text widget:
def escrito(**kwargs):
write_window = Toplevel(root)
#write_window.title(kwargs) (problematic code)
writing_box = tk.Text(write_window, font = ("calibri", 20), width = 60, height = 15, wrap=WORD)
writing_box.pack(expand = tk.YES, fill = tk.X)
writing_box.grid(row = 0, column = 0, sticky = 'nswe')
texto = '\n'.join(key + ":\n" + value for key, value in kwargs.items())
writing_box.insert("1.0", texto)
def septic_osteo():
escrito(**infections.Septic_arthritis)
Septic_arthritis = {
'Empirical Treatment':
'Flucloxacillin 2g IV 6-hourly',
'If non-severe penicillin allergy':
'Ceftriaxone IV 2g ONCE daily',
'If severe penicillin allergy OR if known to be colonised with
MRSA':
'Vancomycin infusion IV, Refer to Vancomycin Prescribing
Policy',
'If systemic signs of sepsis': 'Discuss with Consultant
Microbiologist'
}
So when I run the code, the escrito functions parses the dictionary and writes its content onto a text widget contained on a Toplevel window. What I would like to know is how to dynamically rename the Toplevel window with the dicitonary's name. I do know that I can do this:
def septic_osteo():
escrito(**infections.Septic_arthritis)
write_window.title('Septic_arthritis)
but I do have like 100 functions like the one above, so, aside from labour intensive, I am not sure is the more pythonic way, so, is there a way that the window can be renamed with the dictionary name? (i.e. 'Septic_arthritis)
Thanks
If your data is in an object named infections, with attributes such as Septic_arthritis, the most straight-forward solution is to pass the data and the attribute as separate arguments, and then use getattr to get the data for the particular infection.
It would look something like this:
def escrito(data, infection):
write_window = Toplevel(root)
write_window.title(infection)
writing_box = tk.Text(write_window, font = ("calibri", 20), width = 60, height = 15, wrap="word")
writing_box.pack(expand = tk.YES, fill = tk.X)
writing_box.grid(row = 0, column = 0, sticky = 'nswe')
texto = '\n'.join(key + ":\n" + value for key, value in getattr(data, infection).items())
writing_box.insert("1.0", texto)
The important bit about the above code is that it uses getattr(data, infection) to get the data for the given infection.
If you want to create a button to call this function, it might look something like this:
button = tk.Button(..., command=lambda: escrito(infections, "Septic_arthritis"))
This will call the command escrito with two arguments: the object that contains all of the infections, and the key to the specific piece of information you want to display.
This question already has answers here:
Why does Tkinter image not show up if created in a function?
(5 answers)
Closed 2 years ago.
I'm creating a little program to show if something is ok, but I have a problem with the images. It was working until I create a function to generate my page
Without images
def genTout(matInput):
#images
vert_off = PhotoImage(file=r".\asset\vert_off.png")
rouge_off = PhotoImage(file=r".\asset\rouge_off.png")
vert_on = PhotoImage(file=r".\asset\vert_on.png")
rouge_on = PhotoImage(file=r".\asset\rouge_on.png")
#frame
rightFrame = Frame(highlightbackground="black",highlightthickness=5 ,bg="grey")
buttonControl = Frame(rightFrame,highlightbackground="black",highlightthickness=5 ,bg="grey")
for i in range(0,4):
Label(buttonControl,text=i+1).grid(row=0,column=i+2)
Label(buttonControl,image=(vert_on if matInput[i*2] == 0 else vert_off)).grid(row=1,column=i+2)
Label(buttonControl,image=(rouge_on if matInput[i*2+1] == 0 else rouge_off)).grid(row=2,column=i+2)
return frame
When i take my code on the main it's working but if I put the code inside a function no images appear
Here is my main where I get the return
root = Tk()
PanelView = PanedWindow(width=100, bd=5,relief="raised",bg="grey")
PanelView.pack(fill=BOTH,expand=1)
#my side bar code
...
rightFrame = genTout(matInput)
PanelView.add(rightFrame)
I saw that problem often already here : Your PhotoImage objects are getting garbage collected when the function finishes / returns. Tkinter somehow doesn't like that (for example Button's can get garbage collected, Tkinter somehow deals with it, but that's not the case for PhotoImage's). You must somehow save these images, for example by creating the frame first thing in your function and then making the images attributes of the frame, like this:
def genTout(matInput):
#frame
rightFrame = Frame(highlightbackground="black", highlightthickness=5 , bg="grey")
buttonControl = Frame(rightFrame, highlightbackground="black", highlightthickness=5, bg="grey")
#images
rightFrame.vert_off = PhotoImage(file=r".\asset\vert_off.png")
rightFrame.rouge_off = PhotoImage(file=r".\asset\rouge_off.png")
rightFrame.vert_on = PhotoImage(file=r".\asset\vert_on.png")
rightFrame.rouge_on = PhotoImage(file=r".\asset\rouge_on.png")
for i in range(0, 4):
Label(buttonControl, text=i + 1).grid(row=0, column=i + 2)
Label(buttonControl, image=(rightFrame.vert_on if matInput[i * 2] == 0 else rightFrame.vert_off)).grid(row=1, column=i+2)
Label(buttonControl, image=(rightFrame.rouge_on if matInput[i * 2 + 1] == 0 else rightFrame.rouge_off)).grid(row=2, column=i + 2)
return frame
very new to scripting with python in maya so excuse my limited knowledge.
I need help figuring out how to define the variable for a floatSlider. I need two float sliders for the assignment I'm doing. I need one that will change the size of the selected or specified objects, and I need another that will use MASH to change the count of that object.
I have script with those sliders and a Distribute button laid out. I'm not sure what I need to include to link the scale of the object to the slider I have.
This is the code I have so far:
from maya import cmds
if cmds.window('mainUI2', exists=True):
cmds.deleteUI
win = cmds.window("mainUI2", title="Bush Generator", widthHeight=(300, 300))
# Layout
cmds.columnLayout(adjustableColumn=True)
cmds.text(label='Bush Generator')
cmds.button(label='Distribute', command='DistributeMesh()')
cmds.text(label=' ')
# need help defining Leaf_size
Leaf_size = cmds.floatSlider(min=0, max=100, value=0, step=1)
# I tried another type of slider
LeafScale = cmds.intSliderGrp(min=0, max=100, f=True)
cmds.text(label='Leaf Size')
# need defining Leaf_amount and linking to mash count
Leaf_amount = cmds.floatSlider(min=0, max=100, value=0, step=1)
cmds.text(label='Leaf Amount')
# Bush tool
def DistributeMesh():
cmds.loadPlugin("MASH", quiet=True)
import MASH.api as mapi
count = 3000
source_mesh = "pCube2"
scatter_mesh = "pSphere1"
source_shape = cmds.listRelatives(scatter_mesh, children=True)[0]
cmds.select(source_mesh)
mash_network = mapi.Network()
mash_network.createNetwork(name="Test", geometry="Instancer")
# set to use meshes to scatter
cmds.setAttr(mash_network.distribute + ".arrangement", 4)
cmds.setAttr(mash_network.distribute + ".pointCount", count)
# connect mesh
cmds.connectAttr(
source_shape + ".worldMesh[0]",
mash_network.distribute + ".inputMesh",
force=True)
cmds.showWindow(win)
Scale is a float value so you can use cmds.floatSliderGrp to set the source mesh's scale. First you have to define a separate function that will be triggered when you change the value of floatSliderGrp, then in floatSliderGrp set its changeCommand parameter to that function:
from maya import cmds
# Define a function that will be called when the slider changes values.
def on_size_slider_changed(value):
source_mesh = "pCube2"
if cmds.objExists(source_mesh): # Check if it exists.
cmds.setAttr("{}.scale".format(source_mesh), value, value, value) # Set its scale.
if cmds.window('mainUI2', exists=True):
cmds.deleteUI
win = cmds.window("mainUI2", title="Bush Generator", widthHeight=(300, 300))
# Layout
cmds.columnLayout(adjustableColumn=True)
cmds.text(label='Bush Generator')
cmds.button(label='Distribute', command='DistributeMesh()')
# Use `changeCommand` to define what function it should call.
leaf_size_slider = cmds.floatSliderGrp(label="Size", field=True, min=0, max=100, value=1, changeCommand=on_size_slider_changed)
# Bush tool
def DistributeMesh():
cmds.loadPlugin("MASH", quiet=True)
import MASH.api as mapi
count = 3000
source_mesh = "pCube2"
scatter_mesh = "pSphere1"
source_shape = cmds.listRelatives(scatter_mesh, children=True)[0]
cmds.select(source_mesh)
mash_network = mapi.Network()
mash_network.createNetwork(name="Test", geometry="Instancer")
# set to use meshes to scatter
cmds.setAttr(mash_network.distribute + ".arrangement", 4)
cmds.setAttr(mash_network.distribute + ".pointCount", count)
# connect mesh
cmds.connectAttr(
source_shape + ".worldMesh[0]",
mash_network.distribute + ".inputMesh",
force=True)
cmds.showWindow(win)
Dragging the slider will now set the scale of the cube. Though to be honest the structure of the code here is very messy and a bit too hard-coded (think about how it would work with the current selection instead of explicitly using the object's names)
Let me start by posting some little helper functions I'll use to formulate my questions:
import textwrap
import sys
from pathlib import Path
from PyQt5.Qsci import QsciScintilla
from PyQt5.Qt import * # noqa
def set_style(sci):
# Set default font
sci.font = QFont()
sci.font.setFamily('Consolas')
sci.font.setFixedPitch(True)
sci.font.setPointSize(8)
sci.font.setBold(True)
sci.setFont(sci.font)
sci.setMarginsFont(sci.font)
sci.setUtf8(True)
# Set paper
sci.setPaper(QColor(39, 40, 34))
# Set margin defaults
fontmetrics = QFontMetrics(sci.font)
sci.setMarginsFont(sci.font)
sci.setMarginWidth(0, fontmetrics.width("000") + 6)
sci.setMarginLineNumbers(0, True)
sci.setMarginsForegroundColor(QColor(128, 128, 128))
sci.setMarginsBackgroundColor(QColor(39, 40, 34))
sci.setMarginType(1, sci.SymbolMargin)
sci.setMarginWidth(1, 12)
# Set indentation defaults
sci.setIndentationsUseTabs(False)
sci.setIndentationWidth(4)
sci.setBackspaceUnindents(True)
sci.setIndentationGuides(True)
sci.setFoldMarginColors(QColor(39, 40, 34), QColor(39, 40, 34))
# Set caret defaults
sci.setCaretForegroundColor(QColor(247, 247, 241))
sci.setCaretWidth(2)
# Set edge defaults
sci.setEdgeColumn(80)
sci.setEdgeColor(QColor(221, 221, 221))
sci.setEdgeMode(sci.EdgeLine)
# Set folding defaults (http://www.scintilla.org/ScintillaDoc.html#Folding)
sci.setFolding(QsciScintilla.CircledFoldStyle)
# Set wrapping
sci.setWrapMode(sci.WrapNone)
# Set selection color defaults
sci.setSelectionBackgroundColor(QColor(61, 61, 52))
sci.resetSelectionForegroundColor()
# Set scrollwidth defaults
sci.SendScintilla(QsciScintilla.SCI_SETSCROLLWIDTHTRACKING, 1)
# Current line visible with special background color
sci.setCaretLineBackgroundColor(QColor(255, 255, 224))
# Set multiselection defaults
sci.SendScintilla(QsciScintilla.SCI_SETMULTIPLESELECTION, True)
sci.SendScintilla(QsciScintilla.SCI_SETMULTIPASTE, 1)
sci.SendScintilla(QsciScintilla.SCI_SETADDITIONALSELECTIONTYPING, True)
def set_state1(sci):
sci.clear_selections()
base = "line{} state1"
view.setText("\n".join([base.format(i) for i in range(10)]))
for i in range(0, 10, 2):
region = (len(base) * i, len(base) * (i + 1) - 1)
if i == 0:
view.set_selection(region)
else:
view.add_selection(region)
def set_state2(sci):
base = "line{} state2"
view.setText("\n".join([base.format(i) for i in range(10)]))
for i in range(1, 10, 2):
region = (len(base) * i, len(base) * (i + 1) - 1)
if i == 1:
view.set_selection(region)
else:
view.add_selection(region)
class Editor(QsciScintilla):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
set_style(self)
def clear_selections(self):
sci = self
sci.SendScintilla(sci.SCI_CLEARSELECTIONS)
def set_selection(self, r):
sci = self
sci.SendScintilla(sci.SCI_SETSELECTION, r[1], r[0])
def add_selection(self, r):
sci = self
sci.SendScintilla(sci.SCI_ADDSELECTION, r[1], r[0])
def sel(self):
sci = self
regions = []
for i in range(sci.SendScintilla(sci.SCI_GETSELECTIONS)):
regions.append(
sci.SendScintilla(sci.SCI_GETSELECTIONNSTART, i),
sci.SendScintilla(sci.SCI_GETSELECTIONNEND, i)
)
return sorted(regions)
I've got a couple of questions actually:
Question 1)
if __name__ == '__main__':
app = QApplication(sys.argv)
view = Editor()
set_state1(view)
view.move(1000, 100)
view.resize(800, 300)
view.show()
app.exec_()
I'll get this (you can see the question in the below snapshot):
Question 2)
if __name__ == '__main__':
app = QApplication(sys.argv)
view = Editor()
set_state1(view)
set_state2(view)
view.move(1000, 100)
view.resize(800, 300)
view.show()
app.exec_()
How can I modify the code so I'll be able to restore state1 when pressing ctrl+z?
Right now when using ctrl+z you won't be able to get state1:
mainly because how setText behaves:
Replaces all of the current text with text. Note that the undo/redo
history is cleared by this function.
I've already tried some of the functions posted in the undo and redo docs but no luck so far.
For instance, one of my attempts has been first selecting all text and then using replaceSelectedText and finally restoring the selections from the previous state manually, the result was ugly (i don't want the editor scrolling messing up when undoing/redoing)... Basically, I'd like to get the same feeling than SublimeText.
Btw, this is a little minimal example but in the real-case I'll be accumulating a bunch of operations without committing to scintilla very often... that's why I'm interested to figure out how to rollback to a previous state when using the undoable setText... Said otherwise, i'd like to avoid using Scintilla functions such as insertAt, replaceSelectedText or similars... as I'm using python string builtin functions to modify the buffer internally.
EDIT:
I'm pretty sure beginUndoAction & endUndoAction won't help me to answer question2 but... what about SCI_ADDUNDOACTION? Although the docs are pretty confusing though... :/
Question 1:
The last selection added is automatically set as the Main selection. To remove it, add line sci.SendScintilla(sci.SCI_SETMAINSELECTION, -1) at the end of the set_state1 function.
Question 2:
The way you described it by storing the selections, using the replaceSelectedText, and then using setCursorPosition / reselecting all selections and setFirstVisibleLine to restore the scroll position is one way to go.
Looking at the C++ source of the setText function:
// Set the given text.
void QsciScintilla::setText(const QString &text)
{
bool ro = ensureRW();
SendScintilla(SCI_SETTEXT, ScintillaBytesConstData(textAsBytes(text)));
SendScintilla(SCI_EMPTYUNDOBUFFER);
setReadOnly(ro);
}
You could try setting the text using sci.SendScintilla(sci.SCI_SETTEXT, b"some text"), which doesn't reset the undo/redo buffer.
I'm trying visualise the sine waves addition in python using tkinter, and I'm trying to build lines between each circles center, but what I've tried so far didnt work as I thought it would. is there a way to fix what I've tried (see code), or a way to move only one coordinates point of a line independantly from the other?
As you'll see in the code if you run it, I've tried a method where each iteration the previous line is erased and a new one is created. When I run the code there is actually a line between each center of the circles just like I want, but facts are those lines persist and won't erase themselves; for some reason it seems like the canvas.delete(line) doesnt work as I expected it to.
here's the full code. The interesting part is in the 'updateline' fonction, into 'act()' func.
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()
I expected the lines to disappear once the updateline function called itself again, because of that 'canvas.delete(line)' line into updateline, and I can't really understand why it does that.
anyway if you have a solution to make the lines move, without creating and deleting them each time the function is called, feel free to tell me.
Thanks!
If I understand the problem correctly, I believe the issue is with this code:
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)
It fails to reassign the new line to the line variable for the next call. Instead try:
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)
Which gets rid of the extra lines when I run it. Let me know if I've missed the point.