Python tkinter 画布闪烁

huangapple go评论121阅读模式
英文:

Python tkinter canvas flickering

问题

首先,我应该说明我已经意识到这个网站上有类似名称的其他帖子。我已经查看过它们,但据我所知,它们没有解决我的问题。实际上,我想说我的问题比大多数示例要简单得多。

简单地说,我想创建一个透明的矩形,用来显示拖动选择区域。当我发现tkinter不支持透明度时,我决定简单地绘制四条线来创建一个矩形的外观,而不在这些线之间绘制任何东西。虽然不是理想的解决方案,但对于我的小项目来说足够了(尽管如果有人知道一个不需要太多麻烦就能实现透明度和颜色渐变等功能的库,我很愿意听听意见)。

所以我创建了我的画布。在鼠标按下时,我保存了坐标,并在该坐标处创建了四条线(实际上是一个像素的矩形),在鼠标移动时,我使用canvas.coords方法移动这些线。在鼠标抬起时,我使用canvas.delete删除了这些线。

代码似乎运行正常,但问题似乎在于画布的更新。如果我点击并慢慢拖动,矩形会出现在我想要的位置。如果我移动得更快(我应该澄清,我认为这个拖动速度在正常使用范围内),底部和右侧的线条就会消失,直到鼠标要么减速要么停止。如果我像疯了一样快速移动鼠标,我可能会理解,但发生这种情况的速度真的不是那么快。因此,这些线条明显要么消失,要么闪烁,因为更新偶尔足够快,以跟踪移动的鼠标。

基本上看起来相当糟糕,但有一些需要注意的事情。所以如果我把鼠标向下和向右拖动(使框变大),这种效果就会发生。当我把它拖回原点,以缩小框时,不管我多快都不会发生,这种情况不会发生。我确信这是画布小部件的一些怪癖,但我想要知道如何修复它,或者人们是否已经转向其他库而不是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捕获桌面以显示发生了什么。使用屏幕截图和该程序,我看不到明显的闪烁/消失线条(即使将录制器设置为以60fps录制)。尽管我能够使用手机录制我看到的情况,因此我们可以在这里看到:Vimeo上的闪烁视频

英文:

Firstly I should state that I am aware of other posts on this site with similar names. I have been through them but they do not, so far as I can tell, address my problem. I would actually say my problem is far more simple than most of those examples.

Simply put, I wanted to create a transparent rectangle that I could use to show a drag select area. When I found out that tkinter doesn't do transparency, I decided on the idea of simply drawing four lines to create the appearance of a rectangle without drawing anything between those lines. Not ideal, but for my small project it was enough (although if anyone knows a library that does transparency and colour gradients and things without much fussing around, I'm all ears).

So I created my canvas. On mouse down I saved the coordinates and also created four lines at that coordinate (effectively a one pixel rectangle), and on mouse move I used the canvas.coords method to move the lines. On mouse up I removed the lines, using canvas.delete.

The code seems to work fine, but it's (what I assume is) the updating of the canvas that's the problem. If I click and drag really slowly, the rectangle will appear as I want it to. If I move faster (and I should clarify that I consider this drag speed to be well within normal usage), the bottom and right lines just disappear until the mouse either slows down or stops. Had I been whizzing the mouse around like crazy I might have understood, but the speed at which this happens is really not that fast. So the lines very visibly either disappear, or flicker as occasionally the update is fast enough to keep track of the moving mouse.

Basically it looks pretty bad, however there is something of note. So if I drag my mouse down and to the right (to make the box larger), this effect will happen. When I drag it back to the start, to make a smaller box, it does not occur no matter how fast I try to do it. I'm sure this is some quirk of the canvas widget, but I'd like to know either how to fix it or if people have just gone on to other libraries instead of tkinter (if so, what are they?)

The code is about as simple as it comes for this kind of thing:

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=&#39;green&#39;)
    Line2ID = canvas.create_line(event.x, event.y, event.x, event.y, fill=&#39;green&#39;)
    Line3ID = canvas.create_line(event.x, event.y, event.x, event.y, fill=&#39;green&#39;)
    Line4ID = canvas.create_line(event.x, event.y, event.x, event.y, fill=&#39;green&#39;)

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(&quot;1000x600&quot;)

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

canvas = tk.Canvas(root, width=800, height=600, bg=&#39;white&#39;)
canvas.pack()

canvas.bind(&quot;&lt;ButtonPress-1&gt;&quot;, OnLeftMouseDown)
canvas.bind(&quot;&lt;B1-Motion&gt;&quot;, OnLeftMouseMove)
canvas.bind(&quot;&lt;ButtonRelease-1&gt;&quot;, OnLeftMouseUp)

root.mainloop()

I've had this problem while dragging other items on a canvas too. I wanted a series of visual 'nodes' that were just frames containing information. Dragging them at even a normal speed cut off the right most and bottom most parts of the frame.

Edit: My OS is Windows 7. I have tried the code suggested below by user sciroccorics and have also tried adding to my own code the canvas method 'update_idletasks'. No change. My program has the flicker and sciroccorics' code has the same flicker.
Furthermore I've used Microsoft Expression Encoder 4 to capture the desktop to show what's going on. With screen caps and that program, I get no visible flicking / disappearing lines (even when I set the recorder to record at 60fps). Although I was able to use my phone to record what I was seeing so we can see that here: Vimeo vid of flickering

答案1

得分: 1

这是您示例的最简版本,只使用一个矩形代替四条线(我在画布上添加了一个蓝色圆圈,以显示矩形是透明的)。在我的(相对老旧的)笔记本电脑上,它不显示任何闪烁或消失的边缘。在您的设备上如何?

正如@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()
英文:

Here is the minimal version of your example, using one rectangle instead of four lines (I've added a blue circle on the canvas, to show that the rectangle is transparent). It does not show any flickering or vanishing edges on my (rather old) laptop. How is it on your device?

As noted by @stovfl, tkinter display refreshing is indeed much less efficient on Windows 10 as it used to be on Windows 7 (which was already much less efficient as it has always been on Linux/X11). I added an update_idletasks() on the mouse move callback, as it sometimes improves refreshing errors. You may comment it out, and test if it offers any difference.

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=&#39;white&#39;)
canvas.pack()
canvas.create_oval(200, 200, 400, 400, fill=&#39;blue&#39;)

canvas.bind(&quot;&lt;ButtonPress-1&gt;&quot;, OnLeftMouseDown)
canvas.bind(&quot;&lt;B1-Motion&gt;&quot;, OnLeftMouseMove)
canvas.bind(&quot;&lt;ButtonRelease-1&gt;&quot;, OnLeftMouseUp)

root.mainloop()

huangapple
  • 本文由 发表于 2020年1月6日 02:44:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/59603077.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定