3

I am trying to make a tkinter overrideredirect window round in shape. I've done this so far:

from tkinter import Tk, Canvas, BOTH, PhotoImage
from tkinter.constants import NW, RAISED
import pyautogui as pg

root = Tk()
root.overrideredirect(1)
root.attributes("-topmost", 1)
root.geometry("500x500")

# Creating a canvas for placing the squircle shape.
canvas = Canvas(root, height=500, width=500, highlightthickness=0)
canvas.pack()

def place_center(): # Placing the window in the center of the screen
    global x, y
    reso = pg.size()
    rx = reso[0]
    ry = reso[1]
    x = int((rx/2) - (500/2))
    y = int((ry/2) - (500/2))
    root.geometry(f"500x500+{x}+{y}")

def round_rectangle(x1, y1, x2, y2, radius=25, **kwargs): # Creating a rounded rectangle
        
        points = [x1+radius, y1,
                x1+radius, y1,
                x2-radius, y1,
                x2-radius, y1,
                x2, y1,
                x2, y1+radius,
                x2, y1+radius,
                x2, y2-radius,
                x2, y2-radius,
                x2, y2,
                x2-radius, y2,
                x2-radius, y2,
                x1+radius, y2,
                x1+radius, y2,
                x1, y2,
                x1, y2-radius,
                x1, y2-radius,
                x1, y1+radius,
                x1, y1+radius,
                x1, y1]

        return canvas.create_polygon(points, **kwargs, smooth=True)

place_center()

# Taking a screenshot and adding it to the canvas to create a transparent effect
root.withdraw()
s = pg.screenshot(region=(x, y, 500, 500))
tp = "C:\\Users\\username\\AppData\\Local\\Temp\\bg.png"
s.save(tp)
bg = PhotoImage(file=tp)
canvas.create_image(0, 0, image=bg, anchor=NW)
root.deiconify()
os.remove(tp)

# Creating the squircle
round_rectangle(0, 0, 500, 500, radius=70, fill="#1fa5fe")

root.mainloop()

I use this function to move the window:

def move(event):
    fx = root.winfo_pointerx() - 250
    fy = root.winfo_pointery() - 10
    root.geometry(f"500x500{fx}+{fy}")

I haven't added it into the code because when I move the window, it becomes square again as the screenshot is taken only of a particular region.

Before moving: Round window before moving

After moving: Squared window after moving

How can I make it round even when I move it?

I tried using root.withdraw() and root.deiconify() in a while True: loop, but it causes flickering.

Any help would be appreciated.

Thank you.

E_net4
  • 27,810
  • 13
  • 101
  • 139
IJ_123
  • 457
  • 6
  • 13
  • 2
    after `root = Tk()` place `root.config(bg='grey15')` and `root.attributes('-transparentcolor', 'grey15')` also set canvas background to the same color – Matiiss Aug 19 '21 at 10:08
  • @Matiiss thanks for that, but it still does the same when I move. – IJ_123 Aug 19 '21 at 10:10
  • 1
    did you set canvas bg to the same color because I forgot that you use canvas which will be on top of root so the root color still maters but you also need to set canvas bg to the same color? – Matiiss Aug 19 '21 at 10:10
  • Yes, I've assigned the same color to both, the root and canvas. It's ```grey15```. I also used ```root.attributes('-transparentcolor', 'grey15')``` which you gave, but that didn't work. – IJ_123 Aug 19 '21 at 10:11
  • can you show the current code then please? – Matiiss Aug 19 '21 at 11:01
  • 1
    @IshaanJog I think @ Matiss is saying to remove all of the screenshotting code and add: `root.config(bg="grey15"); canvas.config(bg="grey15"); root.attributes("-transparentcolor", "grey15")` in it's place – TheLizzard Aug 19 '21 at 11:02
  • You have already used the `transparentcolor` attribute, you don't need the *background image* at all. – acw1668 Aug 19 '21 at 11:02
  • @Matiiss The problem is OP still has the code that takes a screenshot of the background and uses that inside their canvas. If OP removes that and follows your instructions, it will work – TheLizzard Aug 19 '21 at 11:03
  • @TheLizzard I missed the screenshot part, I would have never thought about using screenshots to create a transparency effect, actually pretty cool idea, the screenshot should be made in the moving function tho, then it would probably work, but probably not as smooth – Matiiss Aug 19 '21 at 11:05
  • I'm not sure if it's a good approach to fake rounded corners in this manner. If the app user clicks in the rounded parts, they would expect the click to go to the window behind, but since this happens to be a transparent part of a rectangular window, the click would still go to this app window, instead of to some window behind. – fountainhead Aug 19 '21 at 11:09
  • @fountainhead If the user clicks on the part that is influenced by `.attributes("-transparentcolor", ...)` the click will be redirected to the window behind the `tkinter` window. I just tested it. It seems that `.attributes("-transparentcolor", ...)` actually changes the boundaries of the window so it's not a rectangle anymore. – TheLizzard Aug 19 '21 at 11:11
  • Hi Everyone! As @TheLizzard said, I removed the screenshot function and added the ```root.attributes("-transparentcolor", ...)``` given by @Matiiss. It worked! Thank you so much @Matiiss and @TheLizzard ! – IJ_123 Aug 20 '21 at 07:37
  • @Matiiss It was your idea. Do you want to write an answer? If you do, I will vote to close [this](https://stackoverflow.com/q/65111304/11106801) as a duplicate. – TheLizzard Aug 20 '21 at 08:38
  • @TheLizzard added answer, will add more info later (basics are there already), now in a hurry – Matiiss Aug 20 '21 at 10:04
  • So I have another question. How do I make this window resizable? I saw other threads here and was able to resize the window, but after resizing, it becomes like an Ubuntu installation window (rounded in the top, pointed in the bottom). I think I should create a new thread for this, right? – IJ_123 Aug 21 '21 at 07:23
  • Probably yes, ask another question on that, also probably provide the pictures there, could also link to that question here so I can take a look. Btw did you resize from bottom? The issue could be that the image doesn't get resized with the window, so it goes out of visible canvas, so you can't see the rest of the image, the easiest however is to make the window not resizable, but, yes, ask another question and maybe link it here – Matiiss Aug 21 '21 at 08:41
  • Yes, I'm trying to resize it from the bottom. When I resize, it draws the shape again, but it becomes squared when I move it. Here's the new thread created: https://stackoverflow.com/questions/68880788/how-to-resize-a-tkinter-overrideredirect-window-with-a-shape-in-it – IJ_123 Aug 22 '21 at 11:20

2 Answers2

4

how to change window's shape in tkinter (although this should work most certainly on Windows computers, some other OS (macOS, Linux) may have issues meaning that this exact code may not work and not give the desired output). Explanation in code comments:

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


# load image from web but you can of course load it from file, this is
# just so you can run and immediately see the results
url = 'http://media-s3-us-east-1.ceros.com/g3-communications/images/2019/01/15/05eea4b9b9ce010d2dd6b0c063d2f5ca/p1-blob.png?imageOpt=1&fit=bounds&width=893'
data = requests.get(url, stream=True).raw
# just when loading your own image instead of `data` use the path, and resize
# if you need, can obvs change these numbers
image = Image.open(data).resize((600, 600), Image.ANTIALIAS)


root = Tk()
# set root bg color to some rare color because
# the `-transparentcolor` affects all colors on the window, all of them
root.config(bg='grey15')
# just simply set window to the middle, not necessary
root.geometry(f'+{root.winfo_screenwidth() // 2 - 300}+{root.winfo_screenheight() // 2 - 300}')
# the important part, set the transparentcolor to some rare color in this case `grey15`,
# can obvs be sth else depending on your theme
root.attributes('-transparentcolor', 'grey15')
# remove window from window manager
root.overrideredirect(True)

# create canvas and set the bg to the same "rare" color (width and height set
# to image width and height, can be sth different too)
canvas = Canvas(
    root, bg='grey15', width=image.width, height=image.height, highlightthickness=0
)
canvas.pack()
# convert image to PhotoImage for `tkinter` to understand
photo = ImageTk.PhotoImage(image)
# put the image on canvas because canvas supports transparent bg
canvas.create_image(0, 0, image=photo, anchor='nw')

root.mainloop()

The key parts are .attributes('-transparentcolor', ...) (which is the most likely to not work on some OS) and that the shape of the window is formed by an image of that shape (the image should have transparent background (a .png image)). It is also possible to simply draw a shape using Canvas methods (.create_). Also important to remember that the attribute affects all the colors on the window meaning that for example if you were to set the transparentcolor to say white and your background image contained white, it will most likely be transparent too (tho probably won't look as good) so I would suggest using some color that is the least likely to be on the GUI

Matiiss
  • 5,970
  • 2
  • 12
  • 29
  • Hi @Matiiss Thank you so much!. It's now rounded even after moving! – IJ_123 Aug 21 '21 at 06:49
  • I prefer using a canvas shape instead of an image, so I used ```-transparentcolor``` for the canvas and root, and it works! I created the shape using ```canvas.create_polygon(...)``` – IJ_123 Aug 21 '21 at 07:13
2

I believe this will solve your problem.

from tkinter import *
from PIL import ImageTk, Image
import pyautogui as pg


root = Tk()
root.overrideredirect(1)
root.configure(bg="white")
root.attributes("-topmost", 1)
root.attributes("-transparentcolor", "white")
root.geometry("500x500")

label = Label(root, height=900, width=900, bg="white", border=0)
label.pack()


def place_center():  # Placing the window in the center of the screen
    global x, y
    reso = pg.size()
    rx = reso[0]
    ry = reso[1]
    x = int((rx / 2) - (500 / 2))
    y = int((ry / 2) - (500 / 2))
    root.geometry(f"500x500+{x}+{y}")


place_center()

img = Image.open("bg.png")
img = img.resize((300, 300), Image.ANTIALIAS)

imagem = ImageTk.PhotoImage(img)
label.configure(image=imagem)

root.mainloop()
  • You shouldn't import everything from a module, can cause very hard to debug issues – Matiiss Aug 20 '21 at 14:05
  • @Cristielson Silva Hi Thanks for your answer, but I don't want just an image. I want the whole window to be rounded and movable. Anyways, Matiiss and TheLizzard helped me solve the problem. Thanks! – IJ_123 Aug 21 '21 at 06:47
  • ...and I'd prefer creating an independent shape instead of using an image. – IJ_123 Aug 21 '21 at 07:11