0

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='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()

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

  • Works for me **without flicker**. Instead of `.create_line` you can use [Canvas.create_rectangle-method](http://effbot.org/tkinterbook/canvas.htm#Tkinter.Canvas.create_rectangle-method). ***"flicker"***: Am I right if i guess you are on *Windows 10*? – stovfl Jan 05 '20 at 19:55
  • Windows 7, unfortunately. – TheFaithfulLearner Jan 06 '20 at 10:57
  • Could you please verify if using a [`__main__`](https://stackoverflow.com/questions/419163/what-does-if-name-main-do) entry point makes a difference? `root = tk.Tk()` have to be inside the `if __name__ ...` block. – stovfl Jan 06 '20 at 11:04
  • I tried it so that the specific line you mentioned was inside if __name__ == "__main__": There was no difference that I could see. Also it's just a single file for the entire project for test so I doubt it would be run as a module. – TheFaithfulLearner Jan 07 '20 at 14:30
  • ***"would be run as a module"***: Was not about *running as module*. `tkinter`s mainloop uses Threads and on Windows it's recommend to use a `__main__` entry point. OK, the only i can think of are [limit-the-number-of-events-generated-by-widget](https://stackoverflow.com/questions/58906587/how-to-limit-the-number-of-events-generated-by-widget) – stovfl Jan 07 '20 at 14:37

1 Answers1

1

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='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()
sciroccorics
  • 2,357
  • 1
  • 8
  • 21
  • Are you actually running this on *Windows 10*? Seems the same **flicker** issue as the question some days ago, – stovfl Jan 05 '20 at 22:20
  • @stovfl: Yes, I tested the code above on Linux, Win7 and Win10. The display refresh is clearly less smooth on Win10 than for the 2 other OS. I'm not a Tk specialist, so I don't know if Tk on Windows uses the "old" GDI layer or the "new" DIrect2D layer. If it is still working with the deprecated GDI API, it may be an explanation for the degraded performance on Win10 – sciroccorics Jan 06 '20 at 10:31
  • I used this code, and unfortunately I still have the same issues as before. I also edited my post to show my OS (Windows 7). – TheFaithfulLearner Jan 06 '20 at 10:52
  • I checked your video and I confirm that I do NOT observe the same artefacts (the right edge of selection rectangle totally disappears when you drag fast) on my computer (neither with Win7 nor with Win10). Have you tried to update your video driver? – sciroccorics Jan 07 '20 at 20:55
  • I had the problem before on my old GT 740, and recently (last few weeks) I updated my card to a 1650 SUPER, which required an update of drivers. It seems to have been the same on both cards. I tried it on another PC and it worked fine, so I'm not sure what's happening honestly. – TheFaithfulLearner Jan 09 '20 at 11:44