2017-02-22 109 views
0

До сих пор, когда мне нужно было работать с несколькими фигурами на холсте tkinter, они были именно такими - фигурами. Я получаю их теги с canvas.find_all() и манипулирую их геометрией, перемещаясь, изменяя размер и т. Д.
Но я столкнулся с этой проблемой, которую я не могу решить так.
Если я определяю свой собственный класс и начерпываю этот объект на холст, как я могу отслеживать все объекты на холсте, чтобы вызвать их методы?отслеживание собственных объектов на холсте tkinter

Скажем, я определяю класс Bubble, который рисует шампанское на экране. Через каждую секунду я хочу, чтобы он изменил цвет всех пузырьков на другой цвет, используя их методы change_colour.

my_list = [] 
for n in range(10): 
    bubble = Bubble() 
    my_list.append(bubble) 

while True: 
    time.sleep(1) 
    for item in my_list: 
     item.change_colour() 

Я мог бы добавить его в большой «список ола, затем итерацию через него, как я делаю здесь, но и для случаев с большим количеством объектов, это слишком медленно!
Каков правильный способ сделать это?
Как обычно, спасибо за любую помощь!

Как указано, time.sleep() не имеет никакого смысла, но это не проблема, которую я пытаюсь решить.

+0

Очень вообще говоря, это плохая идея, чтобы иметь либо бесконечные циклы или 'sleep' команды в вашей программе Tkinter. Окно не будет обновляться, и пользовательские команды не будут зарегистрированы, пока ваша функция не закончится и управление не вернется к mainloop. Если вы хотите, чтобы что-то происходило периодически, используйте 'root.after' или' root.after_idle'. Попробуйте это и посмотрите, действительно ли ваш метод 'for item in my_list' действительно является проблемой. – Kevin

+0

Альтернативно, если код, который вы используете здесь, на самом деле не является кодом, который вы используете, это всего лишь код, который вы собирались написать, прежде чем вы решите, что он будет слишком неэффективным: преждевременная оптимизация - это корень всего зла. Вы не можете быть уверены, насколько быстрый код действительно есть, если вы не напишете его и не запустите его и не проверите. – Kevin

+0

Спасибо за совет, это не фактический код, который я использую. Я использовал его, чтобы продемонстрировать свой вопрос как можно более четко, но это не имеет отношения к вопросу. Но спасибо за это. – EriktheRed

ответ

0

Мой совет - предоставить каждому элементу, который вы создали как минимум два тега. Один тег будет «пузырьком», чтобы вы могли сразу ссылаться на все пузырьки, а второй был бы тегом, уникальным для каждого пузыря.

Например:

class Bubble(): 
    def __init__(...): 
     self.tag = "b-%d" % id(self) 
     ... 
     canvas.create_oval(..., tags=("bubble", self.tag)) 
     ... 

При том, что вы можете реализовать change_color метод на Bubble класса, как в следующем, который изменит все элементы холста, созданные этим экземпляром класса:

def change_color(self, color): 
    canvas.itemconfigure(self.tag, fill=color) 

Вы можете создать красный пузырь так:

bubble = Bubble() 
bubble.change_color("red") 

Это также позволяет изменять все пузырьки сразу, используя «пузырь» тег:

canvas.itemconfigure("bubble", outline="blue") 

Если вы хотите, чтобы пузырьки мигать, вы должны не создать while петлю. Вместо этого воспользуйтесь циклом, который уже запущен.

Сделайте это, создав функцию, которая сделает все, что вы хотите, и затем запустите это расписание функций снова через after. Например:

def blink(color="red"): 
    canvas.itemconfigure("bubble", fill=color) 
    new_color = "red" if color == "white" else "white" 
    canvas.after(1000, blink, new_color) 

Это приведет к тому, что все пузырьки будут мигать каждую секунду, пока программа работает.

1

Если вы хотите выполнить индивидуальные индивидуальные изменения для каждого элемента (например, изменив цвет каждого элемента на совершенно новый случайный цвет), то вы не сможете сделать ничего лучше, чем повторять по каждому из них, и называть их по отдельности itemconfig.

Однако, если вы хотите внести одинаковые изменения в каждый элемент, вы можете пометить свои товары и позвонить по номеру itemconfig один раз, используя этот тег в качестве своего спецификатора.

Пример:

import Tkinter 
import random 

root = Tkinter.Tk() 
canvas = Tkinter.Canvas(root, width=400, height=400) 
canvas.pack() 

for i in range(1000): 
    x = random.randint(0, 400) 
    y = random.randint(0, 400) 
    canvas.create_oval((x-5,y-5,x+5,y+5), fill="white", tags=("bubble")) 

current_color = "white" 
def change_colors(): 
    global current_color 
    current_color = "white" if current_color == "black" else "black" 
    canvas.itemconfig("bubble", fill = current_color) 
    root.after(1000, change_colors) 

root.after(1000, change_colors) 
root.mainloop() 

Результат:

enter image description here

Однако, как я уже говорил в предыдущем комментарии, я до сих пор считают, что это преждевременная оптимизация. Даже если у вас есть тысяча элементов, итерация через них и их индивидуальная настройка заметно медленнее, чем при использовании тегов. Пример:

import Tkinter 
import random 

root = Tkinter.Tk() 
canvas = Tkinter.Canvas(root, width=400, height=400) 
canvas.pack() 

items = [] 
for i in range(1000): 
    x = random.randint(0, 400) 
    y = random.randint(0, 400) 
    id = canvas.create_oval((x-5,y-5,x+5,y+5), fill="white") 
    items.append(id) 

current_color = "white" 
def change_colors(): 
    global current_color 
    current_color = "white" if current_color == "black" else "black" 
    for id in items: 
     canvas.itemconfig(id, fill = current_color) 
    root.after(1000, change_colors) 

root.after(1000, change_colors) 
root.mainloop() 
+0

Спасибо за ответ Кевин, но, возможно, мой вопрос был неясным. Я пытаюсь вызвать методы на своих объектах на холсте. Я мог бы также использовать другой метод в примере, который, скажем, разбивает мой пузырь на два небольших пузырька – EriktheRed

0

Метод Canvas.find_withtag() возвращает список идентификаторов всех совпадающих объектов, заданных первым аргументом. Вы можете использовать это в сочетании со словарем, чтобы отобразить их обратно в соответствующие экземпляры вашего класса. После этого вы можете вызвать любой из своих методов.

import Tkinter 
import random 

BUBBLE_TAG = 'Bubble' 
current_color = 'white' 

class Bubble(object): 
    def __init__(self, canvas, x, y, size, color): 
     self.canvas = canvas 
     self.id = canvas.create_oval((x-5,y-5,x+5,y+5), fill=color, tags=BUBBLE_TAG) 

    def change_color(self, new_color): 
     self.canvas.itemconfigure(self.id, fill=new_color) 

root = Tkinter.Tk() 
canvas = Tkinter.Canvas(root, width=400, height=400) 
canvas.pack() 

mapping = {} 
for i in range(1000): 
    x, y = random.randint(0, 400), random.randint(0, 400) 
    color = 'black' if random.randint(0, 1) else 'white' 
    obj = Bubble(canvas, x, y, 5, color) 
    mapping[obj.id] = obj 

def change_colors(): 
    for id in canvas.find_withtag(BUBBLE_TAG): 
     current_color = canvas.itemcget(id, 'fill') 
     new_color = 'black' if current_color == 'white' else 'white' 
     mapping[id].change_color(new_color) # calls method of object 
    root.after(1000, change_colors) 

root.after(1000, change_colors) 
root.mainloop() 

Вот пример его работы:

sample of it running