На следующем скриншоте холста показано приложение с 8 нарисованными кругами; 2 и 4 из них соединены линиями; красный круг был выбран (это то, что указывает красный цвет) и готов к соединению с другим кругом при следующем щелчке; один круг не присоединен и не выбран.
make_circle:
щелчок по кнопке выбирает действие «нарисовать круг» и удаляет круги, которые, возможно, уже были выбраны.
когда это действие активно, щелчок по холсту рисует синий круг.
join_circles:
При нажатии активируется действие «присоединиться к кругу» и удаляются круги, которые, возможно, уже были выбраны.
Первый щелчок по кругу на холсте выделяет этот круг, цвет которого меняется на красный; второй щелчок по кружку на холсте выделяет второй кружок, соединяет их в одну линию, сбрасывает цвета на синий и очищает выделение.
Щелчок по пустой части холста ничего не делает.
![enter image description here](https://i.stack.imgur.com/JbKBb.png)
Вам нужно будет отслеживать, какое действие выполнить: нарисовать новый круг или выбрать два круга, чтобы присоединиться. Вам также необходимо отслеживать, сколько кругов было выбрано.
Затем, после успешного рисования линии между кругами, вам нужно будет очистить выделение.
Я решил использовать возможность Python «функции как объект первого класса», чтобы избежать беспорядочного учета состояния: вы выбираете действие, которое нужно выполнить, нажав соответствующую кнопку, затем щелчки на холсте будут связаны с этим действием. .
Следующий код делает это на холсте и печатает, какое действие выбрано, а какое выполняется в консоли:
import tkinter as tk
class CommandButtons(tk.Frame):
def __init__(self, master):
self.master = master
super().__init__(self.master)
self.make_circle_btn = tk.Button(root, text='make_circle', command=make_circle)
self.make_circle_btn.pack()
self.join_circles_btn = tk.Button(root, text='join_circles', command=select_circles)
self.join_circles_btn.pack()
self.pack()
class CircleApp(tk.Frame):
def __init__(self, master):
self.master = master
super().__init__(self.master)
self.canvas = tk.Canvas(root, width=600, height=600, bg='cyan')
self.canvas.pack(expand=True, fill=tk.BOTH)
self.pack()
def make_circle():
_purge_selection()
print('cmd make_circle selected')
c_app.canvas.bind('<Button-1>', _make_circle)
def _make_circle(event):
c_app.canvas.create_oval(event.x - 25, event.y - 25, event.x + 25, event.y + 25, fill="blue")
def select_circles():
_purge_selection()
print('cmd join_circles selected')
c_app.canvas.bind('<Button-1>', _select_circles)
def _select_circles(event):
print(f'select circle {event}')
x, y = event.x, event.y
selection = c_app.canvas.find_overlapping(x, y, x, y)
if selection is not None and len(selected_circles) < 2:
selected_circles.append(selection)
c_app.canvas.itemconfigure(selection, fill='red')
if len(selected_circles) == 2:
if all(selected_circles):
_join_circles()
_purge_selection()
def _join_circles():
coordinates = []
for item in selected_circles:
x, y = find_center(item)
print(x, y)
coordinates.append(x)
coordinates.append(y)
c_app.canvas.create_line(coordinates)
def _purge_selection():
global selected_circles
for item in selected_circles:
c_app.canvas.itemconfigure(item, fill='blue')
selected_circles = []
def find_center(item):
x0, y0, x1, y1 = c_app.canvas.bbox(item)
return (x0 + x1) / 2, (y0 + y1) / 2
if __name__ == '__main__':
selected_circles = []
root = tk.Tk()
command_frame = CommandButtons(root)
c_app = CircleApp(root)
root.mainloop()
Лучшая версия будет использовать класс для инкапсуляции выбранных элементов; здесь я использовал коллекцию как глобальную переменную. Это также правильно обрабатывает выбор перекрывающихся кругов.