5

I have a program that will show an image on screen when a hotkey is pressed on my keyboard. Depending on which key was pressed a different image will be shown. After 3 seconds of no input my root Tk gets withdraw()n so there is nothing on screen.

Now, whenever I want to show an image I call this function:

    def drawOverlay(self, index):
        self.canvas.itemconfig(self.overlayWidget, image=self.overlayImages[index])
        self.deiconify()

The problem is that I see the old image for a few milliseconds before it is replaced.

I tried to find out why there is the delay eventhough I deiconify() only after i switched the image and came across this answer which suggested to call deiconify() using the root's after(). I tried that and when it still didn't work I played around with the call and found out that calling

self.after(1000, self.deiconify())

causes the old image to appear for exactly 1 second after which it is replaced with the new one.

This leads me to believe that the Tk or the Canvas is unable to update while it is withdraw()n. Is there any way around this? I'd rather have a short delay before the image is displayed than the old image flashing on the screen for a few frames but I have no idea how to accomplish this.

Edit: So I'm an idiot for actually calling deiconify instead of just passing it to after because of the parantheses. That does fix the old image appearing for the duration but does not fix the flashing.

Edit2: I managed to reproduce this problem with the following code.

Pressing any key on the keyboard will make a green rectagle appear. Waiting for it to vanish and then pressing another key will make a red rectangle appear.

Only sometimes can you see the flashes happening so try for a couple times. I did not manage to reproduce when -transparentcolor isnt set but I can't tell if this is due to the option being the problem or due to the reduced rendering times making the problem almost imperceptable. It is also a bit easier to see then overrideredirect is set.

Edit3: I have worked the code down further by using actual images. Even without the keypress or additional event callbacks this code produces a black flash before displaing the image. -transparentcolor and overrideredirect(True) seem to be vital to reproducing this. Manually calling update() before reduces the frequency of this ocurring but still causes flashing consistently on larger images. This points to rendering time being one of the factors.

overlay.png

overlayRaw.png

from tkinter import Tk, Canvas
from PIL import ImageTk, Image

TRANSCOLOR = "blue"
IMAGE_SMALL = "overlay.png"
IMAGE_LARGE = "overlayRaw.png"

class Overlay(Tk):
    def __init__(self):
        Tk.__init__(self)

        self.rawImage = Image.open(IMAGE_SMALL)
        self.image = ImageTk.PhotoImage(self.rawImage)
        self.canvas = Canvas(self, width = self.rawImage.size[0], height = self.rawImage.size[1])
        self.canvas.pack()

        self.wm_attributes('-transparentcolor', TRANSCOLOR) # If disabled stops the flashes from ocurring even on large images. 
        self.overrideredirect(True)     # If disabled the Windows animation for opening windows plays. Stops the flashing from ocurring

        self.withdraw()

        self.overlayWidget = self.canvas.create_image(0, 0, image = self.image, anchor = "nw")

        self.deiconify()              # Flashes Clearly Everytime

##        self.update_idletasks()
##        self.deiconify()              # Only Flashes Sometimes. Always flashes on large images

##        self.update()
##        self.deiconify()              # Only Flashes Sometimes. Always flashes on large images

##        self.after(0, self.deiconify) # Flashes Clearly everytime

##        self.after(200, self.deiconify) # Only Flashes Sometimes. Always flashes on large images

##        self.update()
##        self.after(200, self.deiconify) # Flashes Clearly Everytime

o = Overlay()
o.mainloop()
Aviss
  • 63
  • 5
  • `self.after(1000, self.deiconify())` does not do what you think it does. It calls `deiconify` immediately, then sleeps for one second. – Bryan Oakley Jan 30 '18 at 16:51
  • 1
    Please provide a [mcve], and let us know what platform you are running on. – Bryan Oakley Jan 30 '18 at 16:51
  • @BryanOakley I just noticed myself. That does not fix the entire problem unfortunately. I added the source code for my Tk Class. I'm working on Windows 8.1 – Aviss Jan 30 '18 at 17:02
  • Please don't provide all of that code. Create a [mcve]. What you posted has too much irrelevant code. – Bryan Oakley Jan 30 '18 at 17:05
  • @BryanOakley updated again – Aviss Jan 30 '18 at 17:21
  • 1
    Please read what a [mcve] is. Your code isn't complete, nor is it minimum. For example, is `resizable` really part of the problem? – Bryan Oakley Jan 30 '18 at 17:22
  • @BryanOakley sorry for being ignorant. I added a working example. It does depend on `pillow` and `keyboard` though – Aviss Jan 30 '18 at 18:11
  • what happens if you change `after(0, ...)` to `after(100, ...)`? Also, what happens if you an explicit call to `self.update()` _before_ calling `self.deiconify`? – Bryan Oakley Jan 30 '18 at 19:41

1 Answers1

0

I don't see much that could cause a flicker. My recommendation would be to add an explicit call to self.update before the call to deiconify happens. That should instruct tkinter to redraw everything on the canvas. However, that won't help if tkinter on your platform defers drawing until the window is mapped.

Try replacing this:

self.after(0,self.deiconify)
self.after(2000, self.hideOverlay)

with this:

self.update()
self.deiconify()
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • I added a more reliable example. if you can't reproduce this problem with the larger image then `Windows 8.1` seems to be the problem as my laptop gets the same results as my PC – Aviss Jan 31 '18 at 12:56