Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tkinter canvas move leaves a pixel trail

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:

example1

I need to be able to move Points around cleanly, without leaving anything behind.

  • I tried changing the 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.
  • I have tried fill='' and also outline='' in the Canvas.oval config, but this does not help.
  • The behavior of these pixel trials seem erratic, like they will disappear over time, only leaving a limited amount of footprints behind.
  • I have tried removing the time.sleep(.2) from the movement loop, and that seems to make the problem a lot more pronounced.

example2

  • I've found that the only way to cleanup these rogue colored pixels is to run 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.

like image 817
Self Dot Avatar asked Sep 16 '25 04:09

Self Dot


1 Answers

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:

enter image description here

like image 57
Mike - SMT Avatar answered Sep 18 '25 17:09

Mike - SMT