Changing a displayed image inside mainloop on Tkinter? - python

I'm trying to make a program to display a single color, fullscreen. The idea is to use it on a large screen to create environment in the room, the screen changing from color to color after certain time.
Displaying the color fullscreen is not a problem but how can I change said color smoothly?
For the fullscreen display I've used a Tkinter window with the same size as the screen: imagesprite = canvas.create_image(w/2, h/2, image=image) where image is a certain color. But to change the color I need to destroy the window using root.after(2000, root.destroy) and then create a new one. This is not smooth as the desktop can be seen for a brief moment.
How can I change the image displayed inside a Tkinter window on the go, or, how can I close one window and open another one smoothly?

An option you have is instead of using an image is to have a background for the Canvas object. Here is the minimum code to have a single colour background.
from tkinter import Tk, Canvas
root = Tk()
root.attributes("-fullscreen",True)#Makes the window fullscreen
canvas = Canvas(root, width=root.winfo_width(),height=root.winfo_height(), background="red") #Makes a canvas with a red coloured background
#The width and height of the Canvas are taken from the root object
canvas.pack()
root.mainloop()
From here, instead of deleting the window constantly, it is possible to just change the attributes of Tkinter widgets. This is done using the config method.
canvas.config(background="green")
A great thing about tkinter is that you can give it a hex code for the colour and it will draw use that. It needs to be in a string formatted like this:
"#RRGGBB" where each group is a hexadecimal number from 0 to FF.
With this in mind, you can increase the hexadecimal number each frame or however many frames you want between two colours. To have a good transition, you may want to use Hue,Saturation,Value (HSV) colours, and only change the Hue value.
You can store the HSV in a list:
hsv = [0,0.7,0.7]
To convert, you first want to convert to 0 to 255 RGB and then to Hexadecimal.
import colorsys
rgb = colorsys.hsv_to_rgb(*hsv) #Uses list unpacking to give it as arguments
Next, you use the rgb and turn it into Hexcode form.
def getHexCode(rgb):
r = hex(int(rgb[0]*255))[2:] #converts to hexadecimal
#With the hex() function, it returns a number in "0xFE" format (0x representing hex).
#To ignore this, we can take the substring using [2:]
if len(r) < 2: #If the value is a 1-digit number, then we want to add a zero at the front for hexcode form
r = "0"+r
g = hex(int(rgb[1]*255))[2:]
if len(g) < 2:
g = "0"+g
b = hex(int(rgb[2]*255))[2:]
if len(b) < 2:
b = "0"+b
return "#" + r + g + b
Finally, we actually call the change method.
changeSpeed = 200
def changeColor():
rgb = colorsys.hsv_to_rgb(*hsv)
hexCode = getHexCode(rgb)
canvas.config(background = hexCode)
hsv[0]+=0.01
root.after(changeSpeed,changeColor)
root.after(changeSpeed, changeColor)
(EDITED)
Two things that were previously a problem were the root.winfo_width() and root.winfo_height(), as well as the fullscreen giving a border.
To solve the first problem, we have to somehow update the root object, since by default it's 1x1. What we can do for that is make the Canvas object and then update it. It looks like this:
canvas = Canvas(root, width=100,height=100, background="white",highlightthickness=0) #Makes a canvas with a white coloured background
canvas.pack()
canvas.update()
canvas.config(width = root.winfo_width(), height = root.winfo_height())
The second problem is also solved by making the canvas object with a specific attribute, highlightthickness=0. If you notice, the canvas object initialization is now:
canvas = Canvas(root, width=100,height=100, background="white",highlightthickness=0)
Another thing that I thought was useful is if a button closes the program. I bound the "Escape" key to the closing using the following:
def quit(event):
root.destroy()
root.bind("<Escape>", quit)
As a full program, it looks like this:
import colorsys
from tkinter import Tk, Canvas
hsv = [0,1,0.8]
changeSpeed = 200
root = Tk()
root.attributes("-fullscreen",True)
canvas = Canvas(root, width=100,height=100, background="white",highlightthickness=0) #Makes a canvas with a white coloured background
canvas.pack()
canvas.update()
canvas.config(width = root.winfo_width(), height = root.winfo_height())
def getHexCode(rgb):
r = hex(int(rgb[0]*255))[2:]
if len(r) < 2:
r = "0"+r
g = hex(int(rgb[1]*255))[2:]
if len(g) < 2:
g = "0"+g
b = hex(int(rgb[2]*255))[2:]
if len(b) < 2:
b = "0"+b
return "#" + r + g + b
def changeColor():
rgb = colorsys.hsv_to_rgb(*hsv)
hexCode = getHexCode(rgb)
canvas.config(background = hexCode)
hsv[0]+=0.01
root.after(changeSpeed,changeColor)
def quit(event):
root.destroy()
root.after(changeSpeed, changeColor)
root.bind("<Escape>", quit)
root.mainloop()
Some variables you can change in this is the changeSpeed, the original hsv list, and the 0.01 that is added to the hue each increase

Related

How could I display IntVar with tkinter using place()?

When I use the below code which display using .pack(), the number appeared. However when i change to place, there is no number appeared on the screen. How could I use place to display number instead of pack because I want to set it to specific location?
Code with pack() :
import tkinter as tk
master_window = tk.Tk()
master_window.geometry("250x150")
master_window.title("IntVar Example")
integer_variable = tk.IntVar(master_window, 255)
label = tk.Label(master_window, textvariable=integer_variable, height=250)
label.pack()
master_window.mainloop()
Code with place () :
import tkinter as tk
master_window = tk.Tk()
master_window.geometry("250x150")
master_window.title("IntVar Example")
integer_variable = tk.IntVar(master_window, 255)
label = tk.Label(master_window, textvariable=integer_variable, height=250)
label.place( x = 80 , y=80 )
master_window.mainloop()
How could I set the integer variable using place because i want it to display in specific location?
You've set the label to be 250 characters tall. By default the text appears in the centered in the middle of the label. Because of the size it forces the value off screen. If you are able to make the window tall enough, you'll see the number.
If you remove the height attribute from the label it will show up.

Python Tkinter how to adjust the x,y default 0,0 coordinates

I've been learning some basic tkinter and I've come across simple code to centre the window in the middle of your monitor, except when I run it, it's off horizontally. It's pedantic but it bothers me a lot.
The code I used
# Imports
from tkinter import *
# tkinter Application
root = Tk()
#Root Geometry
root_Width = 600
root_Length = 600
# Coordinates of top left pixel of application for centred
x_left = int(root.winfo_screenwidth()/2-root_Width/2)
y_top = int(root.winfo_screenheight()/2-root_Length/2)
root_Pos = "+" + str(x_left) + "+" + str(y_top)
# Window size and position
root.geometry(str(root_Width) + "x" + str(root_Length) + root_Pos)
root.mainloop()
Even if I go basic, and just try to open a window the size of my monitor (1920x1080) at 0,0, it's misaligned horizontally by 8px.
from tkinter import *
root = Tk()
root.geometry("1920x1080+0+0")
root.mainloop()
I took a screenshot of the result:
I have a dual monitor set up so I've added a red line where the right monitor starts. I don't know what the issue is or how to fix it. If I change it to,
root.geometry("1920x1080+-8+0")
It opens where it should, but I want to fix this in general preferably. I want 0,0 to be the top left pixel of the monitor. I acknowledge the issue may not be with python, but any advice would be helpful.
Ok, there are two thing that you are missing. The first, in your first example, is that you need to assure tkinter is getting the right values, and to do this you have to use update_idletasks() method before any winfo.
The second thing, which explains why you have to use -8 to center the full screen window, is that tkinter widows have outer-frames. You can determine the size of this frame by checking for the top left coordinate of the window (winfo_rootx()) and of the outer-frame (winfo_x()). Now the frame width is the difference between both: frame_width = root.winfo_rootx() - root.winfo_x() while the real window width is real_width = root.winfo_width() + (2*frame_width), since you have to consider the frame in both sides.
In summary, to center the window horizontally you do:
width = root.winfo_width()
frame_width = root.winfo_rootx() - root.winfo_x()
real_width = root.winfo_width() + (2*frame_width)
x = root.winfo_screenwidth() // 2 - real_width // 2
(here you can print the frame width and you will see it is 8)
and then use geometry method to place the window at position x.
Off course you can do the same for the vertical alignment

Tkinter PhotoImage Put Method Transparency (on Canvas)

I need to create semi-transparent 1x1 images which I can zoom on the canvas.
Currently, I have a directory full of prerendered transparent images for each possible alpha value, however I would like to avoid that.
Also, I cannot use anything outside of the standard library - this is for a school project and the computers don't have admin access (cannot use PIP).
Using PhotoImage, you can create a blank image and place pixels in the desired coordinates, however when specifying the color used, I can't figure out how to pass in alpha values. The Tcl Tk documentation (https://www.tcl.tk/man/tcl8.7/TkCmd/photo.htm#M53) states you can specify alpha values in colors.
From my understanding, a red color with an alpha value of 0.5 would look like this: #ff0000#0.5
Here's the code I have right now:
import tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(root, width = 500, height = 500, bg = "black", highlightthickness = 0)
canvas.pack()
image = tk.PhotoImage(width = 1, height = 1)
image.put("#ff0000#0.5", (0, 0))
image = image.zoom(30)
canvas.create_image(
0,
0,
image = image,
anchor = "nw"
)
root.mainloop()
Can I place transparent pixels/regions using the put method in Tkinter's PhotoImage? I cannot use PIL.
transparent = "#000000" # color that will be considered as "transparent"
if color != transparent:
image.put(color, (0, 0))

change size of Frame even if there widget python

hi is there any way to change width and height of widget even if there's widget?
i have code like this
form = Tk()
form.geometry("500x500")
def click():
global frame
frame.config(height = 0 ,width = 0)
frame = LabelFrame(form , text = "vaaja")
frame.place(x = 20 , y = 30)
Label(frame, text ="1").grid(row = 0,column = 0 )
Label(frame, text = "2").grid(row = 1 ,column = 0 )
Button(form , text="Click", command = click).place(x = 200 , y = 200)
form.mainloop()
and when I click the button the size of the frame is the same ( I'cant use grid_forget() for labels and then change the size of frame)
Because you are using place, you have two solutions: you can use place to set the width and height to zero, or you can turn geometry propagation off.
Using place to set the width and height
place allows you to define the width and the height of the placed widget, so in your click function you can do this:
def click():
frame.place_configure(width=0, height=0)
Turning geometry propagation off
A frame is resized to fit its contents by something called "geometry propagation". If you turn this off, you can control the size of the frame with the width and height options of the frame itself. Usually it's better to let Tkinter decide the size for you, but sometimes there's a need to have an explicit size, which is why it's possible to turn geometry propagation off.
Since you are using grid to manage the widgets internal to the frame, you need to use grid_propagate(False) to turn geometry propagation off for that frame:
frame.grid_propagate(False)
By doing so, you're responsible for setting the initial width and height of the widget, though you could leave propagation on to get the initial size, then turn it off with the button click in order to work around that issue.
There's an interesting bug (or feature...) in that if you set the width and height to zero, Tkinter won't redraw the window. At least, it doesn't on the Mac. I don't recall the workaround for that because I never, ever need to set a widget to a zero size, but setting it to 1x1 pixel makes it nearly invisible.

Transparency in Tkinter PhotoImage

In my simple game I'm creating I currently have placeholder rectangle objects as graphics. I'm trying to replace them with sprites, but as I understand it Tkinter doesn't have support for PNGs or alpha transparency. I am using Python 3.3, which doesn't work with PIL (and since it is a school project, I am solely trying to use Tkinter as the only external library). Is there a way to use the alpha channel with the supported file formats so that I can have multiple layers of tiles? I just want to filter out the white pixels.
I was able to use an image with transparency. I understand your wish to avoid use of PIL, but the following code works and demonstrates that Tkinter will support formats with transparency.
from Tkinter import Tk, Canvas
import PIL
root = Tk()
tkimg = PIL.ImageTk.PhotoImage('cat1-a.gif')
canvas = Canvas(root, height=600, width=600)
canvas.grid()
def stamp(event):
canvas.create_image(event.x, event.y, image=tkimg)
canvas.bind('<ButtonPress-1>', stamp)
root.mainloop()
To make the white pixels transparent (I am assuming that white means #ffffff) you could use this function below or something like it. This does not require PIL. It has worked for me for pngs, but also will work for gif.
First, make a new blank image the same size as your image.
Second, copy pixel by pixel to the new image (unless the pixel is white).
Set your original image to the new image.
Here is an example of the function being used:
from tkinter import *
def makeTransparent(img, colorToMakeTransparentInHexFormat):
newPhotoImage = PhotoImage(width=img.width(), height=img.height())
for x in range(img.width()):
for y in range(img.height()):
rgb = '#%02x%02x%02x' % img.get(x, y)
if rgb != colorToMakeTransparentInHexFormat:
newPhotoImage.put(rgb, (x, y))
return newPhotoImage
root = Tk()
mycanvas = Canvas(root, width=200, height=200,bg="orange")
mycanvas.pack()
myphotoImage = PhotoImage(file="whitecar.gif")
#set your image to the image returned by the function
myphotoImage = makeTransparent(myphotoImage, "#ffffff")
canvasImage = mycanvas.create_image(100, 100, image=myphotoImage, anchor=CENTER)
root.mainloop()
Here is an example of a white car with a white background:
Here is an example of that car on the canvas using the example program:
So I hope I have answered your question.
I did not use PIL. nothing but the tkinter module.
I only used gif, not png as you asked.
Wherever white is, will now be transparent.
Note:
For whatever reason, processing transparency multiple times with the above function can result in viewing errors in tkinter. Below is a way to remove multiple colors by using a color switching function:
Here is a car:
Here is another function to switch colors, which can be implemented before making a color transparent.
def switchColors(img, currentColor,futureColor):
newPhotoImage = PhotoImage(width=img.width(), height=img.height())
for x in range(img.width()):
for y in range(img.height()):
rgb = '#%02x%02x%02x' % img.get(x, y)
if rgb == currentColor:
newPhotoImage.put(futureColor, (x, y))
else:
newPhotoImage.put(rgb, (x, y))
return newPhotoImage
Here it is in use
root = Tk()
mycanvas = Canvas(root, width=200, height=200,bg="orange")
mycanvas.pack()
myphotoImage = PhotoImage(file="car.png")
myphotoImage = switchColors(myphotoImage,"#db0000","#ffffff") #switch red to white
myphotoImage = switchColors(myphotoImage,"#d9d9d9","#ffffff") #switch greybackground to white
myphotoImage = switchColors(myphotoImage,"#6d6d6d","#ffffff") #switch windshield grey to white
myphotoImage = makeTransparent(myphotoImage,"#ffffff") #make white transparent
canvasImage = mycanvas.create_image(100, 100, image=myphotoImage, anchor=CENTER)
root.mainloop()
And here is the result of that process:
Here is a reference to a similar problem:
How to rotate an image on a canvas without using PIL?
There is a way to use PIL with Python 3 using non-official versions of PIL
Go to http://www.lfd.uci.edu/~gohlke/pythonlibs/ to download it.

Categories

Resources