Перемещение холста Tkinter оставляет пиксельный след - PullRequest
0 голосов
/ 15 января 2019

Я работаю над игрой на холсте Tkinter, где точки перемещаются по экрану. Я помещаю каждую точку в местоположение с tkinter.Canvas.create_oval(...), а затем перемещаю точки с tkinter.Canvas.move(pointID,delta_x,delta_y).

Моя проблема в том, что точки, кажется, оставляют след, когда они перемещаются. Я сделал упрощенный пример, который демонстрирует мою проблему.

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()

И результат примерно такой:

example1

Мне нужно уметь аккуратно перемещать Очки, ничего не оставляя позади.

  • Я попытался изменить функцию move(), чтобы каждая точка удалялась в соответствии с ее тегом и перерисовывалась в новом месте, но это приводило к той же проблеме.
  • Я попытался fill='', а также outline='' в конфигурации Canvas.oval, но это не помогает.
  • Поведение этих пиксельных испытаний кажется странным, так как они со временем исчезнут, оставив лишь ограниченное количество следов.
  • Я попытался удалить time.sleep(.2) из цикла движения, и это, кажется, делает проблему намного более выраженной.

example2

  • Я обнаружил, что единственный способ очистить эти мошеннические цветные пиксели - запустить canvas.delete("all"), так что на данный момент мое единственное решение - удалить все и перерисовать все постоянно. Это не кажется мне хорошим решением.

Какой хороший способ избежать этих "следов пикселей"? Это действительно кажется мне ошибкой, но, возможно, я где-то совершаю ошибку.

Ответы [ 2 ]

0 голосов
/ 15 января 2019

Как ни странно, проблема, кажется, исходит от width=6 в create_oval(), избавление от которой, кажется, решает проблему.То, как вы изначально настраивали create_oval(), создает овал с нулевой областью (то есть не существует) и шириной границы 6 ... что, очевидно, является неприятной комбинацией.

Итак, вот новый код с некоторыми дополнительными модификациями:

from tkinter import Canvas,mainloop,Tk
import numpy as np
import random
import traceback
import time

class Point:
    def __init__(self,uID):
        self.uID = uID
        self.location = np.ones((2)) * 200
        self.color = "#" + "".join([random.choice('0123456789ABCDEF') for j in range(6)])

    def move(self):
        delta = (np.random.random((2)) - .5) * 20
        self.location += delta

    def draw(self,canv):
        x0, y0 = self.location
        canv.create_oval(
             x0, y0, x0 + 2, y0 + 2,
             fill=self.color, outline=self.color,
             tags=('runner'+str(self.uID),'runner')
        )

def repeater(window):
    the_canvas.delete('all')
    for i in points:
        i.move()
        i.draw(the_canvas)
    window.after(25, repeater, window)

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.append(Point(i))

repeater(window)
window.mainloop()

Кроме того, кстати, удаление всех элементов на холсте - способ его очистки.это не расточительно, так как вы все равно обновляете все элементы.

0 голосов
/ 15 января 2019

После некоторых копаний я нашел этот пост здесь: Метод Python3 tkinter.Canvas.move () создает артефакты на экране

Проблема была в овальных границах. Так что я сделал, чтобы удалить границы и сделать овал немного больше, чтобы компенсировать, и, похоже, это сработало.

Если вы измените эту строку:

self.the_canvas.create_oval(200, 200, 200, 200, 
                            fill=self.color, outline=self.color, width=6,
                            tags=('runner' + str(self.uID), 'runner'))

К этому:

self.the_canvas.create_oval(200,200,206,206,
                            fill=self.color,outline='', width=0,
                            tags=('runner'+str(self.uID),'runner'))

проблема должна исчезнуть с потоками или без них.

Если вы хотите посмотреть, как будет выглядеть ваш код без многопоточности, вот пример:

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()

Результаты:

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...