Python мерцание холста tkinter - PullRequest
0 голосов
/ 05 января 2020

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

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

Итак, я создал свой холст. При наведении курсора мыши я сохранил координаты, а также создал четыре линии с этой координатой (фактически прямоугольник в один пиксель), а при движении мыши я использовал метод canvas.coords для перемещения линий. При наведении курсора вверх я удалил линии, используя canvas.delete.

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

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

Код примерно такой же простой, как и для такого рода вещей:

def OnLeftMouseDown(event):
    global InitialX, InitialY, Line1ID, Line2ID, Line3ID, Line4ID
    InitialX = event.x
    InitialY = event.y
    Line1ID = canvas.create_line(event.x, event.y, event.x, event.y, fill='green')
    Line2ID = canvas.create_line(event.x, event.y, event.x, event.y, fill='green')
    Line3ID = canvas.create_line(event.x, event.y, event.x, event.y, fill='green')
    Line4ID = canvas.create_line(event.x, event.y, event.x, event.y, fill='green')

def OnLeftMouseUp(event):
    canvas.delete(Line1ID)
    canvas.delete(Line2ID)
    canvas.delete(Line3ID)
    canvas.delete(Line4ID)

def OnLeftMouseMove(event):
    canvas.coords(Line1ID, InitialX, InitialY, InitialX, event.y)
    canvas.coords(Line2ID, InitialX, InitialY, event.x, InitialY)
    canvas.coords(Line3ID, event.x, InitialY, event.x, event.y)
    canvas.coords(Line4ID, InitialX, event.y, event.x, event.y)


root = tk.Tk()
root.geometry("1000x600")

global InitialX, InitialY
global Line1ID, Line2ID, Line3ID, Line4ID

canvas = tk.Canvas(root, width=800, height=600, bg='white')
canvas.pack()

canvas.bind("<ButtonPress-1>", OnLeftMouseDown)
canvas.bind("<B1-Motion>", OnLeftMouseMove)
canvas.bind("<ButtonRelease-1>", OnLeftMouseUp)

root.mainloop()

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

Редактировать: Моя ОС - Windows 7. Я попробовал код, предложенный ниже пользователем sciroccorics, а также попытался добавить в свой собственный код метод canvas update_idletasks. Без изменений. Моя программа имеет мерцание, и код sciroccorics имеет то же самое мерцание. Кроме того, я использовал Microsoft Expression Encoder 4 для захвата рабочего стола, чтобы показать, что происходит. С помощью экранных колпачков и этой программы я не вижу видимых мерцающих / исчезающих линий (даже когда я настраиваю рекордер на скорость записи 60 кадров в секунду). Хотя я мог использовать свой телефон, чтобы записать то, что я видел, поэтому мы можем увидеть это здесь: Vimeo VID о мерцании

1 Ответ

1 голос
/ 06 января 2020

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

Как отмечает @stovfl, обновление экрана tkinter действительно намного менее эффективно на Windows 10, чем на Windows 7 (что уже было гораздо менее эффективным, чем это всегда было в Linux / X11). Я добавил update_idletasks() к обратному вызову перемещения мыши, поскольку это иногда улучшает ошибки при обновлении. Вы можете закомментировать его и проверить, если он предлагает какие-либо различия.

import tkinter as tk

def OnLeftMouseDown(event):
    global InitialX, InitialY, RectID
    InitialX, InitialY = event.x, event.y
    RectID = canvas.create_rectangle(InitialX, InitialY, InitialX, InitialY)

def OnLeftMouseUp(event):
    canvas.delete(RectID)

def OnLeftMouseMove(event):
    canvas.update_idletasks()
    canvas.coords(RectID, InitialX, InitialY, event.x, event.y)

root = tk.Tk()

canvas = tk.Canvas(root, width=800, height=600, bg='white')
canvas.pack()
canvas.create_oval(200, 200, 400, 400, fill='blue')

canvas.bind("<ButtonPress-1>", OnLeftMouseDown)
canvas.bind("<B1-Motion>", OnLeftMouseMove)
canvas.bind("<ButtonRelease-1>", OnLeftMouseUp)

root.mainloop()
...