I am trying to save a tkinter screen to a file (to later convert into a video).
I can't get the correct position of the canvas for using ImageGrab.
My relevant imports are:
import tkinter
import pyscreenshot as ImageGrab
I am trying to save the screen using (after drawing the screen):
grab = ImageGrab.grab(bbox=canvas.bbox())
ImageGrab.grab_to_file(fileName,grab)
I don't know how to get the canvas position for using "ImageGrab.grab".
Is there any way to get the bounding box for the whole canvas, so as to later use ImageGrab to store a screenshot?
---Edit------------------------------------------------------------------
Solution:
box = (canvas.winfo_rootx(),canvas.winfo_rooty(),canvas.winfo_rootx()+canvas.winfo_width(),canvas.winfo_rooty() + canvas.winfo_height())
grab = ImageGrab.grab(bbox = box)
grab.save(file_path)
You have methods like winfo_x(), winfo_y() to get position inside parent widget (it doesn't have to be main window), and winfo_rootx(), winfo_rooty() to get position on screen.
Effbot.org: Basic Widget Methods
Code displays canvas' position on screen and inside parent Frame
import tkinter as tk
def callback():
print(' root.geometry:', root.winfo_geometry())
print('canvas.geometry:', canvas.winfo_geometry())
print('canvas.width :', canvas.winfo_width())
print('canvas.height:', canvas.winfo_height())
print('canvas.x:', canvas.winfo_x())
print('canvas.y:', canvas.winfo_y())
print('canvas.rootx:', canvas.winfo_rootx())
print('canvas.rooty:', canvas.winfo_rooty())
root = tk.Tk()
tk.Label(root, text='SOME WIDGETS IN ROOT').pack()
frame = tk.Frame(root)
frame.pack()
tk.Label(frame, text='SOME WIDGETS IN FRAME').pack()
canvas = tk.Canvas(frame, bg='green')
canvas.pack()
print('\n--- before mainloop start---\n')
callback()
print('\n--- after mainloop start ---\n')
root.after(100, callback)
root.mainloop()
Example result
--- before mainloop start ---
root.geometry: 1x1+0+0
canvas.geometry: 1x1+0+0
canvas.width : 1
canvas.height: 1
canvas.x: 0
canvas.y: 0
canvas.rootx: 0
canvas.rooty: 0
--- after mainloop start ---
root.geometry: 380x303+770+462
canvas.geometry: 380x267+0+18
canvas.width : 380
canvas.height: 267
canvas.x: 0
canvas.y: 18
canvas.rootx: 770
canvas.rooty: 498
Related
I am trying to create a translucent window in Tkinter like the one in windows 11
How to do this? If we cannot do this can we capture a part of a screen and blur it using cv2 and use it as a continuously updating background?
No, this is not directly possible with tkinter. But:
If you use PIL, you can get the location of the window, and then take a screenshot, then blur it and then make it your app background. But this wont work if user tries to move/resize the application. But here is a rough code:
from tkinter import *
from PIL import ImageTk, ImageGrab, ImageFilter # pip install Pillow
root = Tk()
root.overrideredirect(1) # Hide the titlebar etc..
bg = Canvas(root)
bg.pack(fill='both',expand=1)
root.update()
# Get required size and then add pixels to remove title bar and window shadow
left = root.winfo_rootx()
top = root.winfo_rooty()
right = left + root.winfo_width()
bottom = top + root.winfo_height()
root.withdraw() # Hide the window
img = ImageGrab.grab((left,top,right,bottom)) # Get the bg image
root.deiconify() # Show the window
img = img.filter(ImageFilter.GaussianBlur(radius=5)) # Blur it
img = ImageTk.PhotoImage(img)
bg.create_image(0,0, image=img, anchor='nw') # Show in canvas
label = Label(root,text='This is a translucent looking app')
bg.create_window(bg.winfo_width()/2,bg.winfo_height()/2,window=label) # Position in the center
root.mainloop()
Output with tkinter:
tkinter is not the best choice if you are trying to go for a modern look, use PyQt and check qtacrylic
Output with PyQt:
For live blur (native Windows blur) use "BlurWindow":
python -m pip install BlurWindow
from tkinter import *
from ctypes import windll
from BlurWindow.blurWindow import blur
root = Tk()
root.config(bg='green')
root.wm_attributes("-transparent", 'green')
root.geometry('500x400')
root.update()
hWnd = windll.user32.GetForegroundWindow()
blur(hWnd)
def color(hex):
hWnd = windll.user32.GetForegroundWindow()
blur(hWnd,hexColor=hex)
e = Entry(width=9)
e.insert(0,'#12121240')
e.pack()
b = Button(text='Apply',command=lambda:[color(e.get())])
b.pack()
root.mainloop()
i tried to copy and paste the code from furas (i was looking for something like that):
Vertical scrollbar for frame in Tkinter, Python
This code create:
- a window with tkinter
- a frame
- and a vertical scrollbar
The only issue is that the frame does not fit to the root window. Can you please let me know how to fit the frame to the main window with that code (i changed few things in the code of the link, please find it below)?
no error messages are generated, but if you run it, you will see that the frame does not fit to the root window
Thank you for your help
import tkinter as tk
from tkinter import *
def on_configure(event):
# update scrollregion after starting 'mainloop'
# when all widgets are in canvas
canvas.configure(scrollregion=canvas.bbox('all'))
root = tk.Tk()
root.geometry("1200x1000")
# --- create canvas with scrollbar ---
canvas = tk.Canvas(root)
canvas.pack(side=tk.LEFT)
scrollbar = tk.Scrollbar(root, command=canvas.yview)
scrollbar.pack(side=tk.LEFT, fill='y')
canvas.configure(yscrollcommand = scrollbar.set)
# update scrollregion after starting 'mainloop'
# when all widgets are in canvas
canvas.bind('<Configure>', on_configure)
# --- put frame in canvas ---
frame = tk.Frame(master=root, width=980, height=980)
canvas.create_window((0,0), window=frame, anchor='nw')
#frame.pack_propagate(0) #Don't allow the widgets inside to determine the frame's width / height
#frame.pack(fill=tk.BOTH, expand=True) #Expand the frame to fill the root window
# --- add widgets in frame ---
l = tk.Label(frame, text="Hello", font="-size 50")
l.pack()
l = tk.Label(frame, text="World", font="-size 50")
l.pack()
l = tk.Label(frame, text="Test text 1\nTest text 2\nTest text 3\nTest text 4\nTest text 5\nTest text 6\nTest text 7\nTest text 8\nTest text 9", font="-size 20")
l.pack()
# --- start program ---
root.mainloop()
I'm currently working on a little project on python-3.x including some tkinter ressources. My program is made to display on a screen a list of pictures included in a directory, each picture is put on a button that is a sixth of the original image, and if we click on it, it display the image on his original size on a new window. The original window is set by the amount of pictures i put in the columns (i can choose in the code) and i ve made a scrollbar because i have to work with a lot of pictures.
But here is my problem, it's works fine except that if i change the window size, like reduce it for example, the buttons don't follow, they just vanish behind the window, and with the scrollbar.
I'm not particularly good in python so i was wondering that maybe by doing like a threading we could get the window size in live and then if the window size is inferior/superior of our columns of buttons, we could resize it and change the amount of columns then reload the page, but i will have to work with multiple image so it will take a lot of time.
from tkinter import *
from tkinter.filedialog import *
from tkinter.messagebox import *
from PIL import Image, ImageTk
import tkinter as tk
import glob
import os
import cv2
import copy
import _thread
import time
folder = 'X:/users/Robin/data/dataset-valid/visu/*.jpg'
a=glob.glob(folder)
fic = "../data/list.txt"
fichObj=open(fic,"w")
p = []
for f in a:
fichObj.write(f+"\n")
fichObj.close()
class SuperPhoto(object):
def __init__(self, photo , image):
self.photo = photo
temp = cv2.resize(image, (int((self.photo.width())/6) , int((self.photo.height())/6)))
red = temp[:,:,2].copy()
blue = temp[:,:,0].copy()
temp[:,:,0] = red
temp[:,:,2] = blue
temp = Image.fromarray(temp)
self.miniature = ImageTk.PhotoImage(temp)
def agrandir(self):
Newfen=Toplevel()
Newfen.geometry("+60+60")
#self.photo.resize((500,500))
print(type(self.photo))
label = Label(Newfen, image=self.photo, width=self.photo.width(), height=self.photo.height())
label.image = self.photo # keep a reference!
label.pack()
if os.path.exists (fic): #os.path utile
count = len(open(fic).readlines())
print(count)
#lin = open(fic).readlines()
#print(lin)
class ScrollableCanvas(Frame):
def __init__(self, parent, *args, **kw):
Frame.__init__(self, parent, *args, **kw)
canvas=Canvas(self,bg='#FFFFFF',width=300,height=300,scrollregion=(0,0,500,500))
canvas.update_idletasks()
vbar=Scrollbar(self,orient=VERTICAL)
vbar.pack(side=RIGHT, fill=Y)
vbar.config(command=canvas.yview)
canvas.config(width=1200,height=700)
canvas.config(yscrollcommand=vbar.set)
canvas.pack(side=LEFT,expand=True,fill=BOTH)
# create a frame inside the canvas which will be scrolled with it
self.interior = interior = Frame(canvas)
interior_id = canvas.create_window(0, 0, window=interior, anchor=NW )
# track changes to the canvas and frame width and sync them,
# also updating the scrollbar
def _configure_interior(event):
# update the scrollbars to match the size of the inner frame
size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
canvas.config(scrollregion="0 0 %s %s" % size)
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the canvas's width to fit the inner frame
canvas.config(width=interior.winfo_reqwidth())
interior.bind('<Configure>', _configure_interior)
def _configure_canvas(event):
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the inner frame's width to fill the canvas
canvas.itemconfigure(interior_id, width=canvas.winfo_width())
canvas.bind('<Configure>', _configure_canvas)
class Main_frame(Frame):
# Init
def __init__(self, fenetre_principale=None):
Frame.__init__(self, fenetre_principale)
self.grid()
self.scrollable_canvas = ScrollableCanvas(self)
self.scrollable_canvas.grid(row=1,column=1)
nbCol = 4
for file in a:
image = Image.open(file)
photo = ImageTk.PhotoImage(image)
w = photo.width()
L.append(int(w/6))
#print(L)
sumL = int(sum(L)/nbCol)
print(sumL)
p.append(SuperPhoto(photo, cv2.imread(file)))
for ligne in range(int(count/nbCol)):
for colonne in range(nbCol):
photo = p[ligne * nbCol + colonne]
button = Button(self.scrollable_canvas.interior, image=photo.miniature, command=photo.agrandir)
button.grid(row=ligne, column=colonne)
if __name__ == "__main__":
root = Tk()
root.title("VISU")
root.geometry("+0+0")
L= []
interface = Main_frame(fenetre_principale=root)
root.update_idletasks()
print(root.winfo_width())
print(root.geometry())
interface.mainloop()
So, I except this program to work like a classic directory display, with the columns that change automatically when we resize the window and with the scrollbar that follow it.
If you have any solutions it will really help me ..
You can try it, just put some jpeg pictures in a directory and change the folder variable with the link of your directory.
Thanks in advance for your help, if you have any questions to understand more clearly what i've said don't hesitate.
Each time the root window is resized, a <Configure> event is triggered. Catch it as follows:
def resize(event):
root.update_idletasks()
#update all image sizes here if needed
#all widgets can be 're-grided' here based on new width and height of root window
root.bind('<Configure>', resize)
If you want to ensure that your window cannot be resized, use the following:
root.resizable(False, False)
I want to change the color of rectangle after a certain period of time.
I tried root.after() method but it is not working.
import time
from tkinter import *
def task():
canvas= Canvas()
canvas.create_rectangle(0,0,100,100,fill='red')
canvas.pack()
time.sleep(2)
canvas.create_rectangle(0,0,100,100,fill='blue')
canvas.pack()
time.sleep(2)
print("Testing...")
root = Tk()
canvas = Canvas(root)
while(1):
task()
root.mainloop()
Given code running continuously and root windows get hang for certain time. Rectangle color should change after 2 seconds of delay.
Using time.sleep() hangs the window as well as while loop. To use time.sleep
in tkinter we use after(ms) (ms-milliseconds) in the functions so the GUI won't hang.
While does the same, so we use after(ms, callback, args) function.
Here is an example of what you are trying to achieve. The rectangle will change his color every 1000ms (1 sec) from red to blue - blue to red so on. Also in your code you were creating a new canvas and rectangle every 4 secs. What I did is, I defined one canvas and one rectangle outside the task() function and gave the rectangle a tag (tag='rect') for the reference through which edit that one rectangle's color without creating unnecessary items. I hope this helped you.
Example:
from tkinter import *
root = Tk()
canvas = Canvas(root)
canvas.pack()
canvas.create_rectangle(0,0,100,100,fill='red', tag='rect')
def task():
l = root.after(1000, task)
if int(l.split('#')[1]) % 2 == 0:
canvas.itemconfig('rect', fill='blue')
else:
canvas.itemconfig('rect', fill='red')
task()
root.mainloop()
I am making a wireframing tool desktop application for MacOS in python with Tkinter and I have no idea how to have a text entry bar that I can put in a frame that has a background color of black.
I looked up how to try and do this task, but had no luck. I have also tried to ask my coding class teacher if he can help me with this, but he couldn't figure it out either. Can someone try to help me with this?
Here is what I have so far:
from tkinter import *
root = Tk()
root.geometry("1430x840")
def clear_entry(entry):
entry.delete(0, END)
// here is the text entry
entry = Entry(root)
placeholder_text = 'Title'
entry.insert(0, placeholder_text)
entry.bind("<Button-1>", lambda event: clear_entry(entry))
// here is the frame which I want to put it in
frame2 = Frame(root, width=1430, height=56, bg="#292E30")
frame2.pack()
entry.pack(side=TOP, anchor=N)
root.mainloop()
the end result of what I want, this is an edited image with Preview so I can show you what I want it to look like in the end.
Just move your frame2 up and have the entry master set to frame2. To acquire a fully stretched black frame, pass a few more parameters to your pack method:
from tkinter import *
root = Tk()
root.geometry("1430x840")
frame2 = Frame(root, bg="#292E30")
frame2.pack(fill=X,ipady=15)
def clear_entry(entry):
entry.delete(0, END)
entry = Entry(frame2)
placeholder_text = 'Title'
entry.insert(0, placeholder_text)
entry.bind("<Button-1>", lambda event: clear_entry(entry))
entry.pack(side=LEFT, anchor=N)
root.mainloop()