Python Tkinter - отслеживать и сохранять щелчки мыши для создания элементов холста - PullRequest
0 голосов
/ 02 июля 2018

Я запрограммировал такой метод на холсте:

  • Когда я нажимаю кнопку 1, переменная «состояние» меняется на 0, и каждый щелчок на холсте приводит к кругу
  • Когда я нажимаю кнопку2, переменная «состояние» меняется на 1. Когда элемент, который я щелкнул, представляет собой круг, моя переменная «выбранный» изменяется с Нет на 1, и я также сохраняю координаты моего щелчка мышью

Мой вопрос: как мне кодировать, что питон должен ждать второго клика и посмотреть, не является ли он еще одним кругом? И если да, то как я могу провести линию между ними?

     def newKnotornewEdge(event):
        if self.state == 0:
            self.canvas.create_oval(event.x-25,event.y-25,event.x+25, event.y+25, fill="blue")
            self.canvas.create_text(event.x, event.y, text="A", fill="white")
        elif self.state == 1:
            if self.canvas.itemcget(self.canvas.find_overlapping(event.x, event.y, event.x, event.y), "fill") == "blue":
                self.selected = 1
                start_x = event.x
                start_y = event.y

            else:
                self.selected = None

            if self.selected == 1 and #second mouseclick is a circle too:
                #draw line that connects those circels

    self.canvas.bind("<Button -1>", newKnotornewEdge)

1 Ответ

0 голосов
/ 03 июля 2018

На следующем скриншоте холста показано приложение с 8 нарисованными кругами; 2 и 4 из них соединены линиями; красный круг был выбран (это то, что указывает красный цвет) и готов к соединению с другим кругом при следующем щелчке; один круг не присоединен и не выбран.

make_circle:

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

join_circles:

При нажатии активируется действие «присоединиться к кругу» и удаляются круги, которые, возможно, уже были выбраны.
Первый щелчок по кругу на холсте выделяет этот круг, цвет которого меняется на красный; второй щелчок по кружку на холсте выделяет второй кружок, соединяет их в одну линию, сбрасывает цвета на синий и очищает выделение.
Щелчок по пустой части холста ничего не делает.

enter image description here

Вам нужно будет отслеживать, какое действие выполнить: нарисовать новый круг или выбрать два круга, чтобы присоединиться. Вам также необходимо отслеживать, сколько кругов было выбрано.
Затем, после успешного рисования линии между кругами, вам нужно будет очистить выделение.

Я решил использовать возможность 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()

Лучшая версия будет использовать класс для инкапсуляции выбранных элементов; здесь я использовал коллекцию как глобальную переменную. Это также правильно обрабатывает выбор перекрывающихся кругов.

...