I'm working on a game in a Tkinter canvas where points move around the screen. I place each point at a location with tkinter.Canvas.create_oval(...)
and subsequently move the points with tkinter.Canvas.move(pointID,delta_x,delta_y)
.
My problem is that the points seem to leave a trail behind when they are moved. I made a simplified example that demonstrates my problem.
from tkinter import Canvas,mainloop,Tk
import numpy as np
import random
import traceback
import threading
import time
from queue import Queue
class Point:
def __init__(self,the_canvas,uID):
self.uID = uID
self.location = np.ones((2)) * 200
self.color = "#"+"".join([random.choice('0123456789ABCDEF') for j in range(6)])
self.the_canvas = the_canvas
self.the_canvas.create_oval(200,200,200,200,
fill=self.color,outline=self.color,width=6,
tags=('runner'+str(self.uID),'runner'))
def move(self):
delta = (np.random.random((2))-.5)*20
self.the_canvas.move('runner'+str(self.uID),delta[0],delta[1])
def queue_func():
while True:
time.sleep(.25)
try:
next_action = the_queue.get(False)
next_action()
except Exception as e:
if str(e) != "":
print(traceback.format_exc())
the_queue = Queue()
the_thread = threading.Thread(target=queue_func)
the_thread.daemon = True
the_thread.start()
window = Tk()
window.geometry('400x400')
the_canvas = Canvas(window,width=400,height=400,background='black')
the_canvas.grid(row=0,column=0)
points = {}
for i in range(100):
points[i] = Point(the_canvas,i)
def random_movement():
while True:
for point in points.values():
point.move()
the_queue.put(random_movement)
mainloop()
And the result is something like this:
I need to be able to move Points around cleanly, without leaving anything behind.
move()
function so that each point is deleted according to its tag and redrawn at the new location, but that results in the same problem. fill=''
and also outline=''
in the Canvas.oval
config, but this does not help. time.sleep(.2)
from the movement loop, and that seems to make the problem a lot more pronounced.canvas.delete("all")
, so as of now, my only solution is to delete everything and redraw everything constantly. That doesn't seem like a great solution to me. What's a good way to avoid these "pixel trails"? It really just seems like a bug to me, but maybe I'm making a blunder somewhere.
After some digging I found this post here: Python3 tkinter.Canvas.move() method makes artifacts on screen
The problem there was the borders of the oval. So what I did was remove the borders and make the oval slightly larger to compensate and it looks like that did the trick.
If you change this line:
self.the_canvas.create_oval(200, 200, 200, 200,
fill=self.color, outline=self.color, width=6,
tags=('runner' + str(self.uID), 'runner'))
To this:
self.the_canvas.create_oval(200,200,206,206,
fill=self.color,outline='', width=0,
tags=('runner'+str(self.uID),'runner'))
the problem should go away with or without threading.
If you would like to see what your code would look like without threading here is an example:
import tkinter as tk
import numpy as np
import random
class Point:
def __init__(self, the_canvas, uID):
self.uID = uID
self.location = np.ones((2)) * 200
self.color = "#"+"".join([random.choice('0123456789ABCDEF') for j in range(6)])
self.the_canvas = the_canvas
self.the_canvas.create_oval(200, 200, 206, 206,
fill=self.color, outline='', width=0,
tags=('runner'+str(self.uID), 'runner'))
def move(self):
delta = (np.random.random((2))-.5)*20
self.the_canvas.move('runner'+str(self.uID), delta[0], delta[1])
window = tk.Tk()
window.geometry('400x400')
the_canvas = tk.Canvas(window, width=400, height=400, background='black')
the_canvas.grid(row=0, column=0)
points = {}
for i in range(100):
points[i] = Point(the_canvas, i)
def random_movement():
for point in points.values():
point.move()
window.after(50, random_movement)
random_movement()
window.mainloop()
Results:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With