How do I stop flickering in my transparent Splash screen in Tkinter? - python

Using a bit of modified code I found on the web for creating a generic Tkinter splash screen, I tried to to create a transparent splash screen kind of thing with a .png. I know this code will only work with Windows and I am fine with that.
However, I notice there is flickering (Canvas area is black before it draws the picture) with the image when it draws on the screen. I don't know very much about it but I suspect it has to do something with the buffering of the image after googling and reading. I also read that canvas supports double buffering so the flickering shouldn't be happening so maybe it's the Top Level widget or something.
In any case is there any fix to this? I'd really like to continue using Tkinter for this and it would be a huge letdown not to be able to get rid of the flickering. Here is the code I am using below.
from Tkinter import *
import ttk
from PIL import Image, ImageTk
import time
class Splash:
def __init__(self, root, filename, wait):
self.__root = root
#To use .pngs or .jpgs instead of just .bmps and .gifs, PIL is needed
self.__file = ImageTk.PhotoImage(Image.open(filename))
self.__wait = wait + time.clock()
def __enter__(self):
# Hide the root while it is built.
self.__root.withdraw()
# Create components of splash screen.
window = Toplevel(self.__root)
#Set splash window bg to transparent
window.attributes('-transparent', '#FFFFFE')
#Set canvas bg to transparent
canvas = Canvas(window,bg="#FFFFFE")
splash = self.__file
# Get the screen's width and height.
scrW = window.winfo_screenwidth()
scrH = window.winfo_screenheight()
# Get the images's width and height.
imgW = splash.width()
imgH = splash.height()
# Compute positioning for splash screen.
Xpos = (scrW - imgW) // 2
Ypos = (scrH - imgH) // 2
# Configure the window showing the logo.
window.overrideredirect(True)
window.geometry('+{}+{}'.format(Xpos, Ypos))
# Setup canvas on which image is drawn.
canvas.configure(width=imgW, height=imgH, highlightthickness=0)
canvas.pack()
# Show the splash screen on the monitor.
canvas.create_image(imgW // 2, imgH // 2, image=splash)
window.update()
# Save the variables for later cleanup.
self.__window = window
self.__canvas = canvas
self.__splash = splash
def __exit__(self, exc_type, exc_val, exc_tb):
# Ensure that required time has passed.
now = time.clock()
if now < self.__wait:
time.sleep(self.__wait - now)
# Free used resources in reverse order.
del self.__splash
self.__canvas.destroy()
self.__window.destroy()
# Give control back to the root program.
self.__root.update_idletasks()
self.__root.deiconify()
if __name__ == '__main__':
#thread2 = myLazyDoStuffThread()
root = Tk()
with Splash(root,'splash.png',3):
myprog = ApplyGUIAndOtherThings(root)#,thread2)
root.mainloop()

A rule of thumb you should follow is to never put a call to sleep in a GUI. It does exactly what it says, it causes your whole application to sleep. This means that the GUI is not able to redraw itself, and is likely the cause of your flicker.
If you want a window to be destroyed after a period of time, use the after method. For example:
delta = (self.__wait - now) * 1000
self.after(delta, self.close)
You'll need to define self.close to destroy the window.
This gives you an opportunity to add a little "fade away" effect if you like. You do this by checking to see if the alpha of the splash screen is below some threshold (say, 10%) and destroy it. If it's not, reduce the alpha by 10% and call the function again in 100 ms.

Related

Tkinter window in full width without being zoomed

I'm trying to make a window and because I want to hide the window bar at the top of the screen:
import ctypes
mainWindow = tkinter.Tk()
screen = ctypes.windll.user32
width = int(screen.GetSystemMetrics(0))
height = int(screen.GetSystemMetrics(1))-40
mainWindow.geometry(str(width)+'x'+str(height)+'-0-32') # <----------- signaled line
The problem is that I'm not being abble to get a full width window.
In the signaled line if I write (-0):
mainWindow.geometry(str(width)+'x'+str(height)+'-0
... I get a lack to cover in the right side of the screen.
In the signaled line if I write (+0):
mainWindow.geometry(str(width)+'x'+str(height)+'+0
I get a lack to cover in the left side of the screen.
To hide the window bar (regardless of size) you can call root.overrideredirect(True).
I frequently use this code to fullscreen:
root.overrideredirect(True)
root.geometry('{0}x{1}+0+0'.format(root.winfo_screenwidth(), root.winfo_screenheight()))
you can obviously modify this to format it to just the screenwidth and a constant pixel size height (though I'm unsure of why you'd want to do that)

Tkinter app disappears when exiting full screen

I have a python tkinter application and want to display it in full screen when starting it (in Ubuntu if that matters). So I used Tk.attributes('-zoomed', True). If I pull the window out of full screen with the mouse it just disappears. I can still see the application's icon - so it's still open - but I can only see my empty desktop and clicking on the icon doesn't make it visible either.
When I define the window's size with Tk.geometry('1500x800'), then make it full screen and exit full screen again like before it doesn't disappear.
What could be the reason for that?
Thanks in advance.
edit: I'm sorry, I wrote the question from memory and it's been I while since I wrote that and Tk is not my real variable name. Here is the actual code:
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# Set the window's size
self.geometry('1500x800') # set size manually
# self.attributes('-zoomed', True) # fullscreen with toolbar
# self.attributes('-fullscreen', True) # fullscreen
...
If you want to set the fullscreen attribute to True, it is as easy as:
root = Tk()
root.attributes('-fullscreen', True)
However, it doesn't show the title bar. If you want to keep it visible, you can resize the Tk element with the geometry() method:
root = Tk()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.geometry("%dx%d+0+0" % (w, h))

Desktop Goose like program

i have been trying to create a python program similar to desktop goose in the aspect of a widget moving freely around the screen.
First i tried to create a window with tkinter and make a transparent window
with a PNG picture of a character and then moving the window with win32gui or any other library that would allow me to do this.
But first, the tkinter transparent window thing doesn't work because the widgets inherit the transparency, so there is no way i can display the PNG.
Then i had trouble finding any win32gui function that would allow me to move the windows, i just found stuff that would let me resize them.
Is there any way i can do any of these two tasks?
You can create a transparent window using a transparent PNG image as below:
import tkinter as tk
# select a color as the transparent color
TRNAS_COLOR = '#abcdef'
root = tk.Tk()
root.overrideredirect(1)
root.attributes('-transparentcolor', TRNAS_COLOR)
image = tk.PhotoImage(file='/path/to/image.png')
tk.Label(root, image=image, bg=TRNAS_COLOR).pack()
# support dragging window
def start_drag(event):
global dx, dy
dx, dy = event.x, event.y
def drag_window(event):
root.geometry(f'+{event.x_root-dx}+{event.y_root-dy}')
root.bind('<Button-1>', start_drag)
root.bind('<B1-Motion>', drag_window)
root.mainloop()
Then you can use root.geometry(...) to move the root window.

Python canvas bigger size than screen

I´m trying to figure out how to set the size of my tkinter canvas to bigger then actually my screen is. My screen is 1920x1280, if I set in the following code any higher numbers, the size never gets above this (and I want to do it due to huge drawing there).
Code:
from tkinter import *
class Draw:
def __init__(self, min, max):
self.min = min
self.max = max
def draw(self):
master = Tk()
w = Canvas(master, width=2500, height=2500)
#...
I also tried master.geometry("2500x2500") but that didn´t work either.
You can't make windows larger than the physical screen. However, if your goal is to create a large drawing, you can do that without making the canvas physically large. The canvas widget is just a viewport into a much larger virtual drawing area.
For example, you can create a canvas that is only 400x400 pixels, yet draw an image that is 4000x4000. You can define the size of the virtual window by setting the scrollregion attribute to whatever size you want (up to a limit which I think is maybe around 64000x64000)
This happens because of your screen constraint, you cannot run 2500px * 2500px window on your 1920px * 1280px screen, if you try to run a window 1920px * 1280px on your screen, it would work.
This happens because your limits (2500px * 2500px) is too big for your monitor. The window tries to make 7250000 pixel on you 2457600 pixel screen!
So you would have to get a better screen possibly a 3k or 4k screen to run this.

How do I create a new python tkinter window so fits exactly in the available desktop area, but isn't maximized?

(Environment: Windows, Python 3)
My problem is that using .geometry to set a height & width sets the height & width of the space inside the window - the title bar and window border make it larger. Below is my code so far. As you'll see, even though I have it create a window that's the exact size of the available screen area, the window ends up being too big because the title bar and borders aren't accounted for when setting the size.
See that "what needs to change here" part? What do those two statements need to be changed to or replaced with, that's simple? (If you have it right, the window should fit exactly in the available space on your desktop, with title bar and borders clearly visible.)
Please keep it simple - I'm still new to all of this. :-)
#Important variables:
#screenAvailableWidth, screenAvailableHeight: Height & width of
# screen without
# taskbar
#windowWidth, windowHeight: Height & width of new window to create
#window: Object of class Tk() from tkinter.
#Get screen height and width WITHOUT including the taskbar.
#This part works fine - I've tested the result and it's good.
from win32api import GetMonitorInfo, MonitorFromPoint
monitorInfo = GetMonitorInfo(MonitorFromPoint((0,0)))
workArea = monitorInfo.get("Work")
screenAvailableWidth = workArea[2]
screenAvailableHeight = workArea[3]
#Create a tkinter window
from tkinter import Tk
window = Tk()
#Set new window height & width
#--------------------------------------------
#----- HERE. What needs to change here? -----
#--------------------------------------------
windowWidth = screenAvailableWidth
windowHeight = screenAvailableHeight
#--------------------------------------------
#Show debug info
print("")
print("screenAvailableWidth:",screenAvailableWidth,
" screenAvailableHeight:",screenAvailableHeight)
print("windowWidth:\t",windowWidth," windowHeight:\t",windowHeight)
#Set the new window to upper left corner and height &
# width of screen
window.geometry("{}x{}+0+0".format(windowWidth,windowHeight))
#Show the window
window.mainloop()
I GOT IT! The trick is that as long as the window's visible (even if it's somewhere offscreen), you can do this:
titlebarHeight = window.winfo_rooty() - window.winfo_y()
borderSize= window.winfo_rootx() - window.winfo_x()
And once you have those, you can adjust your desired window width and height to correct for the title bar and borders like this:
WindowWidth = WindowWidth - (borderSize * 2)
WindowHeight = (WindowHeight - titlebarHeight) - borderSize
So, the code that ended up working in the end is this (which is complete - copy and paste this into your editor of choice and it should run as-is):
#Get screen height and width WITHOUT including the taskbar.
#This part works fine - I've tested the result and it's good.
from win32api import GetMonitorInfo, MonitorFromPoint
monitorInfo = GetMonitorInfo(MonitorFromPoint((0,0)))
workArea = monitorInfo.get("Work")
screenAvailableWidth = workArea[2]
screenAvailableHeight = workArea[3]
#Create a tkinter window
from tkinter import Tk
window = Tk()
#Set new window height & width
#--------------------------------------------
#----- HERE. This is what changed: -----
#--------------------------------------------
#Make window visible so we can get some geometry
window.update_idletasks()
#Calculate title bar and border size
titlebarHeight = window.winfo_rooty() - window.winfo_y()
borderSize= window.winfo_rootx() - window.winfo_x()
#Start with full available screen
windowWidth = screenAvailableWidth
windowHeight = screenAvailableHeight
#Adjust for title bar and borders
windowWidth = windowWidth - (borderSize * 2 )
windowHeight = (windowHeight - titlebarHeight) - borderSize
#--------------------------------------------
#Show debug info
print("")
print("screenAvailableWidth:",screenAvailableWidth,
" screenAvailableHeight:",screenAvailableHeight)
print("windowWidth:\t",windowWidth," windowHeight:\t",windowHeight)
#Set the new window to upper left corner and height &
# width of screen (after adjustment)
window.geometry("{}x{}+0+0".format(windowWidth,windowHeight))
#Show the window
window.mainloop()
(The breakthrough was examining all the properties mentioned in the suspected duplicate question (and it's comments and replies) here: tkinter window get x, y, geometry/coordinates without top of window
That question didn't really put the pieces together in a newbie-friendly way, nor did it have any usable sample code, so hopefully this will be of use to someone else in the future.)
To everyone who commented and offered solutions for me, thank you! I really appreciate you taking the time to do so. :-)
Instead of using monitorInfo you can use winfo_screedwidth() and winfo_screenheight().
This is how to do it:
windowWidth = window.winfo_screenwidth
windowHeight = window.winfo_screenheight
window.geometry("%sx%s" %(screenWidth, screenHeight)
You can use window.overrideredirect(True) to get rid of the task bar. It will also get rid of the bar at the top, so you will have to use alt+f4 to exit out of the window.
Also, these options are optional but will certainly make your code more clear and efficient by getting rid of possible errors which may occur when doing certain tasks with the code.
--You can stop reading if you want to--
Instead of from tkinter import Tk use import tkinter as tk. This means you can write tk.widget and not having to write out every widget you use. Also, if you use from tkinter import * you can use import tkinter as tk instead as it groups all attributes into tk. It will certainly clean up the attributes, and you will not having them together with all of the built in attributes
Also, get all of your widgets into a class. You can do this like so:
class Name(tk.Frame):
Inside it you need to write the __init__ function:
def __init__(self, master, **kwargs): #Feel free to add any extra parameters
Inside the __init__ function you write:
super().__init__(master, **kwargs)
The class makes your code neat and tidy while the __init__ and super() are needed for whenever you make a function.
Also, you can use the __name__ == "__main__" if condition to stop the code from running when importing it to another script.
This is how to do this:
def func_name():
root = tk.Tk()
#Add any root titles, geometry or any other configurations here
app = Window(root) #Instead of Window, replace it with your class name
app.pack(fill=tk.BOTH, expand=True)
#Add any app configurations here
root.mainloop()
if __name__ == "__main__":
func_name()
All these features, you can include to make your code neater.

Categories