Python: Tkinter + MSS = mss.exception.ScreenShotError: XDefaultRootWindow() failed - python

I am facing a pretty strange behavior of Python MSS library when using inside Tkinter GUI.
I have a Tkinter window that has a button for popping up a TopLevel window. Inside that TopLevel window, I have a button for taking a screenshot (using MSS). Everything works as supposed for the first time, but the problem happens when I destroy (close TopLevel window) and then open it again to take another screenshot. Then it throws an exception that you will be able to see below. And this only happens when I destroy that TopLevel window and try again.
Edit: it seems the problem is somehow related to Ubuntu (using 20.04), because the problem doesn't exists on Win10.
Exception code:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.8/tkinter/__init__.py", line 1892, in __call__
return self.func(*args)
File "example.py", line 10, in take_screenshot
with mss.mss() as sct:
File "/home/aivaras/Desktop/freelancing/darius/venv/lib/python3.8/site-packages/mss/factory.py", line 41, in mss
return linux.MSS(**kwargs)
File "/home/aivaras/Desktop/freelancing/darius/venv/lib/python3.8/site-packages/mss/linux.py", line 305, in __init__
self.root = self.xlib.XDefaultRootWindow(self._get_display(display))
File "/home/aivaras/Desktop/freelancing/darius/venv/lib/python3.8/site-packages/mss/linux.py", line 191, in validate
raise ScreenShotError(err, details=details)
mss.exception.ScreenShotError: XDefaultRootWindow() failed
The simplified Tkinter code:
import tkinter as tk
import mss
import mss.tools
def take_screenshot():
with mss.mss() as sct:
screen_part = {"top": 370, "left": 1090, "width": 80, "height": 390}
sct_img = sct.grab(screen_part)
mss.tools.to_png(sct_img.rgb, sct_img.size, output="./output.png")
def create_top_level_win():
top_level_win = tk.Toplevel(root)
take_screenshot_btn = tk.Button(top_level_win, text="Take screenshot", command=take_screenshot)
take_screenshot_btn.pack()
root = tk.Tk()
btn = tk.Button(root, text="Open TopLevel", command=create_top_level_win)
btn.pack()
root.mainloop()

Related

_str__ returned non-string (type JpegImageFile) While trying to use image as button in tkinter python

_str__ returned non-string (type JpegImageFile) while trying to use image as button in Tkinter. I tried file= instead of Image.open but that didn't work too. Can someone tell me how to fix this
Here is the simplified version of the code:
from tkinter import *
from PIL import ImageTk, Image
# Window setup
mainwindow = Tk()
mainwindow.geometry('420x420')
mainwindow.title('Root Screen')
# Button image source
temp_img = PhotoImage(Image.open('D:\\Coding\\Python_stuff\\Watch_T800\\temp_img.jpg'))
temp_img_lbl = Label(image=temp_img)
temp_img_lbl.pack()
# Button function
def menufun():
menuwindow = Tk()
menuwindow.geometry('420x420')
menuwindow.title('Menu')
temp = Button(menuwindow, text="Temperature",height=5, width=10, image=temp_img)
temp.grid(row=0, column=0)
# Home Buttons
menu = Button(mainwindow, text="Menu", command=menufun).pack()
# BG Image
bg=Image.open('D:\\Coding\\Python_stuff\\Watch_T800\\background.png')
blah1=ImageTk.PhotoImage(bg)
lbl=Label(mainwindow, image=blah1)
lbl.pack()
mainloop()
And here is the error:
Traceback (most recent call last):
File "d:\Coding\Python_stuff\Watch_T800\test.py", line 14, in <module>
temp_img_lbl = Label(image=temp_img)
^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\yashw\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 3204, in __init__
Widget.__init__(self, master, 'label', cnf, kw)
File "C:\Users\yashw\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 2628, in __init__
self.tk.call(
TypeError: __str__ returned non-string (type JpegImageFile)
I already explained what I tried in the , But I'm telling it again
I have tried using file= instead of image.open but it says
Pyimage1 Not found

Custom Tkinter Images

I'm trying to create a back button. I have an image called back-button.png in the folder img.
This is my code:
from tkinter import *
import customtkinter as ctk
root = Tk()
ctk.CTkLabel(root,
text = 'This is a label',
text_font =('Verdana', 17)).pack(side = LEFT, pady = 11)
img = PhotoImage(file="./img/back-button.png")
ctk.CTkButton(root, image = img).pack(side = LEFT)
root.mainloop()
When I run this code I get this error:
Traceback (most recent call last):
File "c:\Users\User\Desktop\youtube-audio-downloader\tempCodeRunnerFile.py", line 11, in <module>
ctk.CTkButton(root, image = img).pack(side = LEFT)
File "C:\Users\User\AppData\Local\Programs\Python\Python39\lib\site-packages\customtkinter\customtkinter_button.py", line 102, in __init__
self.draw()
File "C:\Users\User\AppData\Local\Programs\Python\Python39\lib\site-packages\customtkinter\customtkinter_button.py", line 147, in draw
self.canvas.configure(bg=CTkColorManager.single_color(self.bg_color, self.appearance_mode))
File "C:\Users\User\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 1646, in configure
return self._configure('configure', cnf, kw)
File "C:\Users\User\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 1636, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: unknown color name "."
So, why is this happening? And how can I display an image on a button?
The problem is that the CtkButton widget doesn't not accept parameters the same way as standard widgets. The first parameter to a CtkButton is the background color, but you're passing the root window and the root window isn't a valid color.
You need to explicitly assign the root window to the master argument.
ctk.CTkButton(master=root, image = img).pack(side = LEFT)
# ^^^^^^^
You are getting error because you are using built-in image define method which is in Tkinter not in Customtkinter. Don't confuse these two. If you want to use Customtkinter only use that because when you are making bigger projects than this, it will be a whole mess if you use these two together.
import customtkinter
from PIL import Image, ImageTk
window = customtkinter.CTk()
button_image = customtkinter.CTkImage(Image.open("assets/yourimage.png"), size=(26, 26))
image_button = customtkinter.CTkButton(master=window, text="Text will be gone if you don't use compound attribute",image=button_image)
image_button.pack()
window.mainloop()

Trying to call my model in Tkinter but it's not waiting until the button is clicked

This is what I tried and ended up getting. I am trying to call my model in Tkinter but it's not waiting until the button is clicked.
import tkinter as tk
import os
var = tk.IntVar()
root = tk.Tk()
class model():
if var == 1:
os.system('python script.py')
button = tk.Button(root, text="Click Me", command=lambda: var.set(1))
button.place(relx=.5, rely=.5, anchor="c")
print("waiting...")
button.wait_variable(var)
print("done waiting.")
Result of the code above:
Traceback (most recent call last):
File ".\test.py", line 3, in <module>
var = tk.IntVar()
File "C:\Users\laksh\anaconda3\lib\tkinter\__init__.py", line 529, in __init__
Variable.__init__(self, master, value, name)
File "C:\Users\laksh\anaconda3\lib\tkinter\__init__.py", line 335, in __init__
self._root = master._root()
AttributeError: 'NoneType' object has no attribute '_root'```
The tk.Tk() call starts the tcl interpreter, so no tk objects can be created before you call it. So move tk.Tk() call above the tk.IntVar() call.
root = tk.Tk()
var = tk.IntVar()
I think your problem here is that you're not importing the Tk module. Try also importing Tk by adding this line.
From Tkinter import *
Also, the module is called Tk, so you'll want to capitalize the T in tk
Hope this helped!

Why do I get a PhotoImage error when I put my GUI into a thread?

I'm trying to come up with a system to display a splash page while the first page of my main GUI(both made with tkinter) loads up. I read online that tkinter had to have all the graphic commands for a particular tkinter window in the same thread. So my idea to display the splash screen in a Toplevel window on a second thread while the main page loads up on the first thread(the main page is hidden while splash page is shown using withdraw()), wouldn't work out. So I decided to have separate GUI's for the splash and the main application. The main page will initialize while the splash is displayed and after that, the splash would be destroyed and the main page would reappear using deiconify(). I tried the idea using 2 simple GUI's and it worked but when I implemented it on my code for the splash page, for some reason, I got the following error.
Exception in thread Thread-1:
Traceback (most recent call last):
File "C:\Users\jovan\AppData\Local\Programs\Python\Python38-32\lib\threading.py", line 932, in _bootstrap_inner
self.run()
File "C:\Users\jovan\AppData\Local\Programs\Python\Python38-32\lib\threading.py", line 870, in run
self._target(*self._args, **self._kwargs)
File "c:/Jovan/Captain!!Project/Captain_repo/rough.py", line 7, in import_
import rough1
File "c:\Jovan\Captain!!Project\Captain_repo\rough1.py", line 71, in <module>
splash.display()
File "c:\Jovan\Captain!!Project\Captain_repo\rough1.py", line 56, in display
self.tkimage = ImageTk.PhotoImage(image1)
File "C:\Users\jovan\AppData\Local\Programs\Python\Python38-32\lib\site-packages\PIL\ImageTk.py", line 112, in __init__
self.__photo = tkinter.PhotoImage(**kw)
File "C:\Users\jovan\AppData\Local\Programs\Python\Python38-32\lib\tkinter\__init__.py", line 4061, in __init__
Image.__init__(self, 'photo', name, cnf, master, **kw)
File "C:\Users\jovan\AppData\Local\Programs\Python\Python38-32\lib\tkinter\__init__.py", line 4006, in __init__
self.tk.call(('image', 'create', imgtype, name,) + options)
RuntimeError: main thread is not in main loop
Exception ignored in: <function PhotoImage.__del__ at 0x04EF02B0>
Traceback (most recent call last):
File "C:\Users\jovan\AppData\Local\Programs\Python\Python38-32\lib\site-packages\PIL\ImageTk.py", line 118, in __del__
name = self.__photo.name
AttributeError: 'PhotoImage' object has no attribute '_PhotoImage__photo'
I found questions online with similar errors but they all suggested things like the image passed to PhotoImage was not a PIL image, which did not help me much. The splash page works fine if I simply execute it or import it from another file but when I put it into a thread, I get the above error. Here is a reproducible code for my problem.
'''The code is executed here.'''
import tkinter as tk
import time
import threading
def import_() :
import rough1
# creating and starting thread to display splash
splash_thread = threading.Thread(target=import_)
splash_thread.daemon = True
splash_thread.start()
# Main GUI
root = tk.Tk()
root.withdraw()
root.geometry("300x300")
root.protocol("WM_DELETE_WINDOW",root.quit())
root.title("rough")
root.update()
tk.Button(root,text="Hey").pack()
tk.Label(root,text="Hello").pack()
root.update_idletasks()
# Re-displays the main GUI if the splash has been destroyed.
print(threading.active_count())
while threading.active_count() == 2 :
print("loading...")
time.sleep(0.5)
else :
root.deiconify()
print(threading.active_count())
root.mainloop()
The code for splash page:
import tkinter as tk
from PIL import Image, ImageTk
import tkinter.ttk as ttk
import time
from ttkthemes import ThemedTk
class SplashScreen(ThemedTk) :
def __init__(self,**kwargs) :
ThemedTk.__init__(self,theme="black",**kwargs)
self.overrideredirect(True)
# self.title("[GAME INITIALIZING...]")
self.splash()
self.update()
self.window()
def splash(self) :
self.image1 = Image.open('project_pics\\splash.png')
def window(self) :
width, height = self.image1.size
setwinwidth = (self.winfo_screenwidth()-width)//2
setwinheight = (self.winfo_screenheight()-height)//2
self.geometry(f"{width}x{height}+{setwinwidth}+{setwinheight}")
self.update()
def display(self) :
print("splash_Displayed!!")
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
splash_canvas = tk.Canvas(self,width=screen_width,height=screen_height)
splash_canvas.pack()
self.update_idletasks()
self.tkimage = ImageTk.PhotoImage(self.image1)
self.update_idletasks()
splash_canvas.create_image(0,0,image=self.tkimage,anchor="nw")
splash_canvas.image = self.tkimage
self.update_idletasks()
splash_canvas.create_text(200,34,text="[LOADING]...PLEASE WAIT",font=("small fonts",20),fill="white")
progressbar = ttk.Progressbar(orient=tk.HORIZONTAL, length=900, mode='determinate',style="Horizontal.TProgressbar")
progressbar.place(x=450,y=25)
progressbar.start()
self.after(5050,self.destroy)
self.mainloop()
splash = SplashScreen()
splash.display()
So these are my queries. Why do I get the above error and how do I fix it??

How do I change the background of a Frame in Tkinter?

I have been creating an Email program using Tkinter, in Python 3.3.
On various sites I have been seeing that the Frame widget can get a different background using Frame.config(background="color").
However, when I use this in my Frames it gives the following error:
_tkinter.TclError: unknown option "-Background"
It does not work when doing the following:
frame = Frame(root, background="white")
Or:
frame = Frame(root)
frame.config(bg="white")
I can't figure it out.
I would post my whole source code but I dont want it exposed on the internet, but the frame creation goes something like this:
mail1 = Frame(self, relief=SUNKEN)
mail1.pack()
mail1.place(height=70, width=400, x=803, y=109)
mail1.config(Background="white")
I have tried multiple options trying to modify the background. The frame is like a wrap around an email preview for an inbox.
In case it's needed, this the way I am importing my modules:
import tkinter, time, base64, imaplib, smtplib
from imaplib import *
from tkinter import *
from tkinter.ttk import *
The following is the full traceback:
Traceback (most recent call last):
File "C:\Users\Wessel\Dropbox\Python\Main\Class Ginomail.py", line 457, in <module>
main()
File "C:\Users\Wessel\Dropbox\Python\Main\Class Ginomail.py", line 453, in main
app = Application(root) #start the application with root as the parent
File "C:\Users\Wessel\Dropbox\Python\Main\Class Ginomail.py", line 60, in __init__
self.initINBOX()
File "C:\Users\Wessel\Dropbox\Python\Main\Class Ginomail.py", line 317, in initINBOX
mail1.config(bg="white")
File "C:\Python33\lib\tkinter\__init__.py", line 1263, in configure
return self._configure('configure', cnf, kw)
File "C:\Python33\lib\tkinter\__init__.py", line 1254, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: unknown option "-bg"
Gives the following error with the code from the answer:
File "C:\Users\Wessel\Dropbox\Python\Main\Class Ginomail.py", line 317, in initINBOX
mail1 = Frame(self, relief=SUNKEN, style='myframe')
File "C:\Python33\lib\tkinter\ttk.py", line 733, in __init__
Widget.__init__(self, master, "ttk::frame", kw)
File "C:\Python33\lib\tkinter\ttk.py", line 553, in __init__
tkinter.Widget.__init__(self, master, widgetname, kw=kw)
File "C:\Python33\lib\tkinter\__init__.py", line 2075, in __init__
(widgetName, self._w) + extra + self._options(cnf))
_tkinter.TclError: Layout myframe not found
Solved! Thanks. Its the inbox bar to the right, background needed to be white.
The root of the problem is that you are unknowingly using the Frame class from the ttk package rather than from the tkinter package. The one from ttk does not support the background option.
This is the main reason why you shouldn't do wildcard imports -- you can overwrite the definition of classes and commands.
I recommend doing imports like this:
import tkinter as tk
import ttk
Then you prefix the widgets with either tk or ttk :
f1 = tk.Frame(..., bg=..., fg=...)
f2 = ttk.Frame(..., style=...)
It then becomes instantly obvious which widget you are using, at the expense of just a tiny bit more typing. If you had done this, this error in your code would never have happened.
You use ttk.Frame, bg option does not work for it. You should create style and apply it to the frame.
from tkinter import *
from tkinter.ttk import *
root = Tk()
s = Style()
s.configure('My.TFrame', background='red')
mail1 = Frame(root, style='My.TFrame')
mail1.place(height=70, width=400, x=83, y=109)
mail1.config()
root.mainloop()

Categories