Можно ли использовать кнопки tkinter для повторного изменения объектов canvas?

#python #tkinter #tkinter-canvas

#python #tkinter #tkinter-canvas

Вопрос:

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

Я пытаюсь поменять местами координаты p1, p2 и p3 в приведенном ниже коде `

 def rf(p1, p2, p3):
    p4=p3
    p3=p2
    p2=p4
def ro(p1, p2, p3):
    p4=p1
    p1=p2
    p2=p3
    p3=p4
    print(p4)
def buttons(p1, p2, p3):
    l=tk.Button(top, text="Rotate", command=ro(p1, p2, p3))
    k=tk.Button(top, text="Reflect", command=rf(p1, p2, p3))
    triangle(p1, p2, p3)
    text(p1, p2)
    k.pack()
    l.pack()
buttons(p1, p2, p3)
top.mainloop()
  

`

когда я запускаю его и нажимаю кнопку поворота, p1 печатает с исходным значением и печатает только один раз, независимо от того, сколько раз нажата кнопка. Как я могу это исправить, и есть ли лучший пакет, который действительно сможет это анимировать? Спасибо!

Ответ №1:

Вы не можете передавать аргументы командам в такой кнопке. То, что вы сделали, на самом деле оценивает ro (p1, p2, p3) при создании кнопки, принимает возврат этого метода (который равен None) и сохраняет его как команду. Итак, когда вы нажимаете кнопку, она вызывает None …. что, конечно, ничего не делает.

Обычно, когда вы создаете кнопку, команда должна содержать только ИМЯ МЕТОДА, например: l = tk.Button(top, text=»Rotate», command=ro)

Это особенность Python, где вы можете обрабатывать целые методы так, как будто они являются переменными. Так что, возможно, сделайте это и измените метод, чтобы он был примерно таким:

 def ro():
    p4=p1
    p1=p2
    p2=p3
    p3=p4
    print(p4)
  

Если вам действительно нужно передавать аргументы, это наиболее распространенный обходной путь:

 l = tk.Button(top, text="Rotate", command=lambda: ro(p1, p2, p3)
  

Существует довольно длинное объяснение того, почему это работает, вы можете посмотреть, что делает ключевое слово lambda в python, чтобы лучше понять его.

Редактировать, как указано в комментариях:

Использование ваших переменных таким образом, каким вы являетесь, может привести к возникновению некоторых странных вещей. А именно, что он не будет «сохранять» изменения в переменных. Это происходит потому, что переменные, с которыми вы работаете, имеют область действия, ограниченную функцией, в которой они используются. То есть используемые вами имена создаются в функции, поэтому они удаляются после завершения функции. Есть несколько способов исправить это.

Один из способов сделать это — сохранить имена глобально, удалить аргументы и сообщить функции, что они являются глобальными:

 p1 = 5  # these are just examples, make sure they are established
p2 = 7  # on the root level, or at least on the level that your
p3 = 12 # button is made or lower

def ro():
    global p1, p2, p3 # this will insist on using the globally named variables and not things specific to this function
    p1, p2, p3 = p2, p3, p1 # oh, btw, you can do this to save space and not have to make a p4
  

В этом случае вам придется отказаться от лямбда-выражения и просто вызвать ‘ro’ в качестве команды.
Это будет делать то, что вы хотите, но имеет пару проблем:

  1. вы не можете использовать метод ro() ни для чего другого, кроме поворота этих трех вещей
  2. использование ключевого слова ‘global’ обычно не одобряется, и я лично никогда его не использую

Чем вы можете быть довольны, так это чем-то более похожим на это:

 p = [14, 12, 83]
q = [1, 43, 18, 75]

def ro(x):
    first = x.pop(0)
    x.append(first)

print(p)
ro(p)
print(p)
print()
print(q)
ro(q)
print(q)
  

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

 l=tk.Button(top, text="Rotate", command=lambda: ro(p))
  

и это изменит список, предоставленный команде ro(), и вам не нужно будет беспокоиться о том, в какой области видимости находится переменная, потому что вы передаете ее с ‘p’ в ro(p) , и это вносит изменения во все, что передается.

Я надеюсь, что это поможет.

Комментарии:

1. Спасибо, это работает с лямбдой, однако, похоже, что это не приводит к постоянному изменению значения p1, которое необходимо для перехода к следующему значению поворота, нет предложений по этому поводу?

2. Да, Python имеет несколько запутанных уровней области видимости для переменных, и я уверен, что именно это здесь и происходит. Я отредактирую свой ответ, чтобы я мог объяснить его немного подробнее, чем может сделать комментарий, подобный этому.

3. Большое вам спасибо, прошло некоторое время с тех пор, как я работал на python, в основном использую C, поэтому я довольно незнаком с соглашениями о глобальных переменных! —теперь помечено как правильное!