Использовать событие мыши tkinter

У меня есть 2 события на холсте по щелчку мыши.

один на холсте ...

self.canvas.bind( '<ButtonPress-1>', self.left_mouse_down )

другое на фигуре на холсте ...

self.canvas.tag_bind( self.shape, '<Button-1>', self.on_left_click )

Проблема, с которой я столкнулся, заключается в том, что оба события запускаются. Есть ли способ использовать событие щелчка на самой фигуре (надеюсь, без использования глобальной переменной)?

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

martineau 01.05.2018 17:19

Вместо того, чтобы связывать обработчики событий с каждым объектом, почему бы просто не привязать событие к холсту и проверить тег CURRENT. См. Пример здесь

scotty3785 01.05.2018 17:40

Я заметил, что в связанной документации написано: «Более простое решение - не допустить, чтобы Tkinter распространял событие на другие обработчики; просто верните строку “break” из вашего обработчика событий». Это тоже звучит многообещающе.

martineau 01.05.2018 17:41

@martineau Да, похоже, сначала последовательно обрабатывается событие tag_bind.

scotty3785 01.05.2018 18:05

@martineau, похоже, перерыв не работает.

Bhupen 01.05.2018 19:20

@martineau: возврат "break" не работает для привязок к элементам на холсте, потому что привязки к элементам холста не используют механизм тегов привязки.

Bryan Oakley 01.05.2018 19:24

Хм, думаю, это снова план А (мое первое предложение), тогда ...

martineau 01.05.2018 19:25

@martineau В обоих случаях event.widget возвращает только холст. Время для плана C. В любом случае спасибо!

Bhupen 01.05.2018 19:30

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

Bryan Oakley 01.05.2018 19:33
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
9
874
2

Ответы 2

Нет механизма, предотвращающего обработку события виджетом, когда и холст, и элемент холста привязаны к событию.

Из канонической документации:

If bindings have been created for a canvas window using the bind command, then they are invoked in addition to bindings created for the canvas's items using the bind widget command. The bindings for items will be invoked before any of the bindings for the window as a whole.

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

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

Вот пример второй техники:

import tkinter as tk
import random

def on_click(event):
    current = event.widget.find_withtag("current")
    if current:
        item = current[0]
        color = canvas.itemcget(item, "fill")
        label.configure(text = "you clicked on item with id %s (%s)" % (item, color))
    else:
        label.configure(text = "You didn't click on an item")

root = tk.Tk()
label = tk.Label(root, anchor = "w")
canvas = tk.Canvas(root, background = "bisque", width=400, height=400)
label.pack(side = "top", fill = "x")
canvas.pack(fill = "both", expand=True)

for color in ("red", "orange", "yellow", "green", "blue", "violet"):
    x0 = random.randint(50, 350)
    y0 = random.randint(50, 350)
    canvas.create_rectangle(x0, y0, x0+50, y0+50, outline = "black", fill=color)
    canvas.bind('<ButtonPress-1>', on_click)

root.mainloop()

Это полезно знать. Кстати, а где «каноническая документация»? effbot? Исходный файл?

martineau 01.05.2018 19:42

@martineau: каноническая документация по tk написана для языка Tcl, который довольно легко переводится на python. Вы можете найти его здесь: tcl.tk/man/tcl8.5/TkCmd/contents.htm. Документация о том, как перевести его на tkinter, находится в разделе Хранитель жизни Tkinter официальной документации python.

Bryan Oakley 01.05.2018 19:57

Спасибо за ссылки. Ваш второй вариант также работает наоборот. т.е. не привязывать обработчик событий к самому виджету Canvas в целом, а вместо этого использовать canvas.tag_bind() и привязывать его к каждому элементу холста индивидуально. Похоже, что в некоторых обстоятельствах это может быть лучше, в зависимости от того, что еще происходит.

martineau 01.05.2018 21:03

@martineau: наоборот, это не сработает, потому что, если вы привяжетесь к объекту или тегу, он будет _только _ срабатывать, когда вы щелкаете по объекту холста. Он не сработает, если вы нажмете на холст в целом (если вы не нарисуете объект, который покрывает весь холст в качестве фона).

Bryan Oakley 01.05.2018 21:15

Да, я полагаю, вы правы, особенно с учетом того, что OP имеет разные обработчики для каждого типа событий (подразумевая, что они хотят различать два и обрабатывать их по-разному).

martineau 01.05.2018 22:07

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

martineau 02.05.2018 19:21

Здесь есть что-то, что очень похоже на второй метод, упомянутый в @Bryan Oakley's отвечать, за исключением того, что делает связывает обработчик событий с каждым элементом холста, а не с самим виджетом Canvas.

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

import tkinter as tk
import random

BKGR_TAG = '_background'
BKGR_COLOR = 'bisque'
RECT_SIZE = 50
WIDTH, HEIGHT = 400, 400

def on_click(event):
    current = event.widget.find_withtag('current')
    if current:
        item = current[0]
        tags = canvas.gettags(item)
        if BKGR_TAG in tags:
            msg = 'You clicked the background'
            # Do other things like call event handler for whole canvas...
        else:
            color = canvas.itemcget(item, 'fill')
            msg = 'You clicked on item with id %s (%s)' % (item, color)
        label.configure(text=msg)

root = tk.Tk()
label = tk.Label(root, anchor='w')
canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT)
label.pack(side='top', fill='x')
canvas.pack(fill='both', expand=True)

# Create background rect same size as canvas.
bkgr = canvas.create_rectangle(0, 0, WIDTH, HEIGHT, width=0, fill=BKGR_COLOR,
                               tags=BKGR_TAG) # Marked with special tag.
canvas.tag_bind(bkgr, '<ButtonPress-1>', on_click)

for color in ('red', 'orange', 'yellow', 'green', 'blue', 'violet'):
    x0 = random.randint(RECT_SIZE, WIDTH-RECT_SIZE)
    y0 = random.randint(RECT_SIZE, HEIGHT-RECT_SIZE)
    id = canvas.create_rectangle(x0, y0, x0+RECT_SIZE, y0+RECT_SIZE,
                                 outline='black', fill=color)
    canvas.tag_bind(id, '<ButtonPress-1>', on_click)

root.mainloop()

Другие вопросы по теме