Tkinter program displays blank screen on linux, works on windows - python-3.x

I've been writing this code for a slideshow program in Linux. The problem I'm having is that when run from a windows environment it works perfectly (Full-screen resized images), however when run from a virtual Linux (Ubuntu x64) environment a blank white canvas appears with no images being displayed.
The code:
from PIL import Image, ImageTk
import tkinter as tk
import os
import glob
import random
class App(tk.Tk):
def __init__(self, image_files, delay):
tk.Tk.__init__(self)
self.w = self.winfo_screenwidth()
self.h = self.winfo_screenheight()
self.overrideredirect(1)
self.geometry("%dx%d+0+0" % (self.w, self.h))
self.delay = delay
self.pictures = []
self.track_img_ndex = 0
for img in image_files:
self.pictures.append(img)
self.picture_display = tk.Label(self)
self.picture_display.pack(expand=True, fill="both")
def show_slides(self):
if self.track_img_ndex < len(self.pictures):
x = self.pictures[self.track_img_ndex]
self.track_img_ndex +=1
original_image = Image.open(x)
resized = original_image.resize((self.w, self.h),Image.ANTIALIAS)
new_img = ImageTk.PhotoImage(resized)
self.picture_display.config(image=new_img)
self.picture_display.image = new_img
self.title(os.path.basename(x))
self.after(self.delay, self.show_slides)
else:
print("End of list!")
delay = 5000
playlist = glob.glob(r'\mnt\hgfs\E\Images\*.*')
random.shuffle(playlist)
image_files = playlist
app = App(image_files, delay)
app.show_slides()
app.mainloop()
Any help would be appreciated!

winfo_screenwidth() and winfo_screenheight() give not correct display size on Linux. Use full-screen mode to fix it.
The code:
from PIL import Image, ImageTk
import tkinter as tk
import os
import glob
import random
class App(tk.Tk):
def __init__(self, image_files, delay):
tk.Tk.__init__(self)
# start window size (any)
self.w = 500
self.h = 500
self.attributes("-fullscreen", True) # Activate full-screen mode
# Change full-screen mode bind
self.bind("<F11>", lambda event: self.attributes("-fullscreen",
not self.attributes("-fullscreen")))
# Bind full-screen mode exit
self.bind("<Escape>", lambda event: self.attributes("-fullscreen", False))
self.geometry("%dx%d+100+100" % (self.w, self.h))
self.delay = delay
self.pictures = []
self.track_img_ndex = 0
for img in image_files:
self.pictures.append(img)
self.picture_display = tk.Label(self)
self.picture_display.pack(expand=True, fill="both")
def show_slides(self):
if self.track_img_ndex < len(self.pictures):
x = self.pictures[self.track_img_ndex]
self.track_img_ndex += 1
original_image = Image.open(x)
window_size_now = (self.winfo_width(), self.winfo_height()) # Getting current window size :
resized = original_image.resize(window_size_now, Image.ANTIALIAS)
new_img = ImageTk.PhotoImage(resized)
self.picture_display.config(image=new_img)
self.picture_display.image = new_img
self.title(os.path.basename(x))
self.after(self.delay, self.show_slides)
else:
print("End of list!")
_delay = 5000
playlist = glob.glob(r'/home/yaroslav_admin/PycharmProjects/LearnPython_01/imgs/*.*')
random.shuffle(playlist)
_image_files = playlist
app = App(_image_files, _delay)
app.after(100, app.show_slides) # delayed start,
# because tkinter need some time to count full-screen window size in main loop !
app.mainloop()

Related

I want to display images that are send to a specific folder in a sort of slideshow

I have a piece of code right now that when i start it up it creates a black canvas and displays a image on it (With Tkinter) that i randomly grab from a folder i have selected with photos in it.
But right now i have to press esc to go to the next image. I would like it to be on a timer.
It also won't go to the next image. It says pyimage2 does not exist.
It would be great if someone can help me. I am incredibly new to python and don't know that much.
from PIL import Image
import os, random
import time
import sys
if sys.version_info[0] == 2:
import Tkinter
tkinter = Tkinter
else:
import tkinter
from PIL import Image, ImageTk
i = True
def showPIL(pilImage):
root = tkinter.Tk()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.overrideredirect(1)
root.geometry("%dx%d+0+0" % (w, h))
root.focus_set()
root.bind("<Escape>", lambda e: (e.widget.withdraw(), e.widget.quit()))
canvas = tkinter.Canvas(root,width=w,height=h)
canvas.pack()
canvas.configure(background='black')
imgWidth, imgHeight = pilImage.size
if imgWidth > w or imgHeight > h:
ratio = min(w/imgWidth, h/imgHeight)
imgWidth = int(imgWidth*ratio)
imgHeight = int(imgHeight*ratio)
pilImage = pilImage.resize((imgWidth,imgHeight), Image.ANTIALIAS)
image = ImageTk.PhotoImage(pilImage)
imagesprite = canvas.create_image(w/2,h/2,image=image)
root.mainloop()
while i == True:
timer = 0
while timer < 5:
path = "C:/Users/thymen.hummel/PycharmProjects/HelloWorld/Images"
random_filename = random.choice([x
for x in os.listdir(path)
if os.path.isfile(os.path.join(path, x))
])
pilImage = Image.open(f'C:/Users/thymen.hummel/PycharmProjects/HelloWorld/Images/{random_filename}')
showPIL(pilImage)
print(random_filename)
timer += 1
time.sleep(5)
check https://www.geeksforgeeks.org/python-convert-speech-to-text-and-text-to-speech/. Instead of typing, you can name the name of the image and the program will open it.
To do it by timer, you can do it using pygame.time.wait(). It takes input in milliseconds. see https://www.pygame.org/docs/ref/time.html for more information.

display video camera on two different windows tkinter python

i have a GUI python application that contain two different function windows and i'am trying to display video camera in the second window of tkinter application.
But, the video frame always be showing on the first windows!! i don't know if it is a problem of threads, or camera librairy. is there any fault in my code?
thanks in advance.
# add the necessairy librairy
import tkinter as tk
import threading
import time
import subprocess
from imutils.video import VideoStream
import time
import imutils
import cv2
import argparse
from PIL import Image
from PIL import ImageTk
class PhotoBoothApp:
def __init__(self,vs):
# store the video stream object and output path, then initialize
# the most recently read frame, thread for reading frames, and
# the thread stop event
self.vs = vs
self.frame = None
self.thread = None
self.stopEvent_2 = None
# initialize the root window and image panel
self.root = tk.Tk()
self.panel = None
self.user_input = tk.StringVar(self.root)
self.but_frame = tk.Frame(self.root)
w = 800 # width for the Tk root
h = 500 # height for the Tk root
# get screen width and height
ws = self.root.winfo_screenwidth() # width of the screen
hs = self.root.winfo_screenheight() # height of the screen
# calculate x and y coordinates for the Tk master window
x = (ws/3) - (w/3)
y = (hs/3) - (h/3)
# set the dimensions of the screen
# and where it is placed
self.root.geometry('%dx%d+%d+%d' % (w, h, x, y))
# when open the second window i want it to be on toplevel; it means when i click outside the frame it won't get hide
self.root.title("Test thread")
# create a button, that when pressed, will take the current
# frame and save it to file
btn = tk.Button(self.but_frame, bd = '5',text=" photo ")
self.but_frame.pack(side="left")
# start a thread that constantly pools the video sensor for
# the most recently read frame
self.stopEvent_2 = threading.Event()
self.thread = threading.Thread(target=self.videoLoop, args=())
self.thread.start()
# set a callback to handle when the window is closed
self.root.wm_protocol("WM_DELETE_WINDOW", self.onClose)
def videoLoop(self):
# DISCLAIMER:
# I'm not a GUI developer, nor do I even pretend to be. This
# try/except statement is a pretty ugly hack to get around
# a RunTime error that Tkinter throws due to threading
try:
# keep looping over frames until we are instructed to stop
while not self.stopEvent_2.is_set():
# grab the frame from the video stream and resize it to
# have a maximum width of 300 pixels
self.frame = self.vs.read()
self.frame = imutils.resize(self.frame, width=300)
#cv2.imshow('video player', self.frame)
# OpenCV represents images in BGR order; however PIL
# represents images in RGB order, so we need to swap
# the channels, then convert to PIL and ImageTk format
image = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)
image = Image.fromarray(image)
image = ImageTk.PhotoImage(image)
# if the panel is not None, we need to initialize it
if self.panel is None:
self.panel = tk.Label(image=image)
self.panel.image = image
self.panel.pack(side="left", padx=10, pady=10)
# otherwise, simply update the panel
else:
self.panel.configure(image=image)
self.panel.image = image
except RuntimeError :
print("[INFO] caught a RuntimeError_________________________________________")
def onClose(self):
# set the stop event, cleanup the camera, and allow the rest of
# the quit process to continue
print("[INFO] closing...")
self.stopEvent_2.set()
self.vs.stop()
self.root.quit()
# here's the main window
Mafenetre = tk.Tk()
#set main window title
Mafenetre.title("GUI")
Mafenetre['bg']='white' # couleur de fond
# get screen width and height
wf1= Mafenetre.winfo_screenwidth()
hf1= Mafenetre.winfo_screenheight()
A = str(wf1)
B = str(hf1)
# set the dimensions of the screen
# and where it is placed
w = 500 # width for the Tk root
h = 500 # height for the Tk root
# get screen width and height
ws = Mafenetre.winfo_screenwidth() # width of the screen
hs = Mafenetre.winfo_screenheight() # height of the screen
# calculate x and y coordinates for the Tk master window
x = (ws/2) - (w/2)
y = (hs/2) - (h/2)
# set the dimensions of the screen
# and where it is placed
Mafenetre.geometry('%dx%d+%d+%d' % (w, h, x, y))
# add test button to the main window
reglage = tk.Button(Mafenetre, bd = '5',text=" PARAM ", bg='#c42034', fg='white',font= "Helvetica", 12))
reglage.pack(side=tk.TOP, padx=6,pady=35)
camera = VideoStream(0).start()
pba = PhotoBoothApp(camera)
Mafenetre.mainloop()

why the recording file is empty in python

I am trying to record my desktop screen and record the audio from microphone simentensously with the help of this python script.
Now when i run only the screen capture part the code works perfectly fine but when i try to run both sound recording and screen capture combine code as shown in below code. my video and audio both file does not open. (0 bytes) or very less size file.
How can i solve this problem.
updated Code
now i am able to record voice properly but the video file is not opening?
import datetime
import tkinter as tk
from tkinter import *
from tkinter import ttk ,FLAT
from PIL import Image, ImageTk, ImageGrab
import cv2
import numpy as np
import threading
import win32api
import pyaudio
import wave
VIDEO_SIZE = (800 , 420) #(960, 540)
p = ImageGrab.grab()
a, b = p.size
chunk = 1024
sample_format = pyaudio.paInt16
channels = 2
fs = 44100
frames = []
g = pyaudio.PyAudio()
date = datetime.datetime.now()
filename='rec_%s-%s-%s-%s-%s-%s.mp4' % (date.year, date.month, date.day,date.hour, date.minute, date.second)
#fourcc = cv2.VideoWriter_fourcc(*'XVID')
frame_rate = 24
cap = cv2.VideoCapture(0)
out = cv2.VideoWriter()
def screen_capturing():
global capturing
capturing = True
while capturing:
img = ImageGrab.grab()
frame = np.array(img)
sc = np.array(img)
_xs,_ys = win32api.GetCursorPos()
cv2.circle(frame,(_xs,_ys),20,(255,255,0), 2)
sc = cv2.resize(sc, VIDEO_SIZE)
tkimage.paste(Image.fromarray(sc))
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
out.write(frame)
def start_screen_capturing():
if not out.isOpened():
out.open(filename, 0x31637661, frame_rate,(VIDEO_SIZE))
t1=threading.Thread(target=screen_capturing, daemon=True)
t1.start()
def stop_screen_capturing():
global capturing
capturing = False
out.release()
def voice_recording():
global recording
while recording:
data = stream.read(chunk)
frames.append(data)
def start_voice_recording():
global stream
stream = g.open(format=sample_format,channels=channels,rate=fs,frames_per_buffer=chunk,input=True)
global recording
recording = True
print('capturing')
t2 = threading.Thread(target=voice_recording) # daemon=True
t2.start()
def stop_voice_recording():
#global recording
recording =False
print(' complete')
filename='test'
filename = filename+".wav"
wf = wave.open(filename,'wb')
wf.setnchannels(channels)
wf.setsampwidth(g.get_sample_size(sample_format))
wf.setframerate(fs)
wf.writeframes(b''.join(frames))
wf.close()
root = tk.Tk()
root.title('Screen Recorder')
root.geometry('+260+70')
tkimage = ImageTk.PhotoImage(Image.new('RGB', VIDEO_SIZE, (0,0,0)))
w, h = VIDEO_SIZE
vbox = tk.Label(root, image=tkimage, width=w, height=h, bg='black')
vbox.pack(pady=10,padx=25)
frame = tk.Frame(root)
frame.pack()
start_cap = tk.Button(frame, text='Start screen Recording', width=30, command=start_screen_capturing)
start_cap.grid(row=0, column=0)
stop_cap = tk.Button(frame, text='Stop screen Recording', width=30, command=stop_screen_capturing)
stop_cap.grid(row=0, column=1)
start_voice = tk.Button(frame, text='Start voice Recording', width=30, command=start_voice_recording)
start_voice.grid(row=0, column=2)
stop_voice = tk.Button(frame, text='Stop voice Recording', width=30, command=stop_voice_recording)
stop_voice.grid(row=0, column=3)
root.mainloop()

Flickering video for tkinter video

I am trying to make a simple play/pause application in tkinter. Basically I want to show a video and have a play/pause button underneath.
So, after some research I found this suitable post to show a video using tkinter and opencv:
to show video streaming inside frame in tkinter
When using the code, given in the accepted answer to show a video, there is no problem and I don't see any flickering. Here is the code:
# import the necessary packages
from __future__ import print_function
import tkinter as tk
from PIL import ImageTk, Image
import cv2
root = tk.Tk()
# Create a frame
app = tk.Frame(root, bg="white")
app.grid()
# Create a label in the frame
lmain = tk.Label(app)
lmain.grid()
# Capture from camera
cap = cv2.VideoCapture(r'PATH_TO_VIDEO_FILE')
# function for video streaming
frame_number = 0
def video_stream():
global frame_number
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
success, frame = cap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image=img)
lmain.imgtk = imgtk
lmain.configure(image=imgtk)
lmain.after(1, video_stream)
frame_number += 1
video_stream()
root.mainloop()
Now, I slightly altered the code to be able to use the grid manager and add a play button:
# import the necessary packages
from __future__ import print_function
import tkinter as tk
from PIL import ImageTk, Image
import cv2
class PhotoBoothApp:
def __init__(self, path_to_video):
# initialize the root window
self.window = tk.Tk()
self.window.title("Video_Player")
self.videocap = cv2.VideoCapture(path_to_video)
self.frame_number = 0
# Initalize
self.videocap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_number)
success, self.frame = self.videocap.read()
cv2image = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGBA)
self.img = Image.fromarray(cv2image)
self.imgtk = ImageTk.PhotoImage(image=self.img)
# Show frames
self.picture_label = tk.Label(self.window, image=self.imgtk, relief=tk.RIDGE).grid(row=0, column=0)
self.btn_next_image=tk.Button(self.window, text="Play", width=50, bg ="green",command=self.video_stream).grid(row=1,column=0)
self.window.mainloop()
def video_stream(self):
self.videocap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_number)
sucess, frame = self.videocap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
self.imgtk = ImageTk.PhotoImage(image=img)
self.picture_label = tk.Label(self.window, image=self.imgtk, relief=tk.RIDGE).grid(row=0, column=0)
# Update Frame Number to display
self.frame_number = self.frame_number + 1
self.window.after(1, self.video_stream)
ph = PhotoBoothApp(r'PATH_TO_FILE')
The problem is that when I execute the above code, the video flickers as if tkinter need to reload something in-between frames. I have no clue why this happens.
P.S. This post here Flickering video in opencv-tkinter integration did not help me.
You need to make two changes: split your self.picture_label line to create a proper reference to your Label object, and then use self.picure_label.config(...) to change the image.
class PhotoBoothApp:
def __init__(self, path_to_video):
# initialize the root window
...
self.picture_label = tk.Label(self.window, image=self.imgtk, relief=tk.RIDGE)
self.picture_label.grid(row=0, column=0)
...
def video_stream(self):
...
img = Image.fromarray(cv2image)
self.imgtk = ImageTk.PhotoImage(image=img)
self.picture_label.config(image=self.imgtk)
# Update Frame Number to display
self.frame_number = self.frame_number + 1
self.window.after(1, self.video_stream)
ph = PhotoBoothApp(r'PATH_TO_FILE')

python tkinter image layers (paste / unpaste image on background)

i have a background image using tkinter canvas,
and i'm adding images on top of it.
so far so good it works well. but what i would like to do is to be able to remove some of the forground images on demand. and when i remove some of them i would like to see the background behind them as it were before adding those forground images on it.
that would be like: paste 5 foreground images and then remove 1 or 2 of them.
so this program i have to far, adds little white filled circles at random position.
if i keep a handle on every little white circles (i can put them in variables and have them all in a list, and get their coordinates later for example). how can i remove some of them and get to see my background behind the removed whites circles ?
is it even possible ?
#!/usr/bin/env python3
from tkinter import *
from PIL import Image, ImageTk
from random import *
class App(object):
def __init__(self):
self.root = Tk()
self.canvas = Canvas(self.root, height=222, width=227)
self.canvas.grid()
# small nature landscape
self.backgnd = PhotoImage( file = "images/nature.png" )
# small white circle
self.mycloud = PhotoImage( file = "images/white.png" )
backgnd_width = (self.backgnd.width()/2)
backgnd_height = (self.backgnd.height()/2)
self.canvas.create_image(backgnd_width,backgnd_height,image=self.backgnd)
def cloud(self):
pos_x = randint(1,220)
pos_y = randint(1,220)
self.canvas.create_image(pos_x,pos_y, image=self.mycloud)
app = App()
app.cloud()
app.cloud()
app.cloud()
app.cloud()
app.cloud()
app.root.mainloop()
in case it might help others here's a working solution.
i added a button that will remove each object placed on the canvas, one a a time.
(thanks for the help, Bryan Oakley)
#!/usr/bin/env python3
from tkinter import *
from PIL import Image, ImageTk
from tkinter import ttk
from random import *
class App(object):
def __init__(self):
self.root = Tk()
self.canvas = Canvas(self.root, height=300, width=227)
self.canvas.grid()
self.mylist=[]
self.backgnd = PhotoImage( file = "images/nature.png" )
self.mycloud = PhotoImage( file = "images/white.png" )
backgnd_width = (self.backgnd.width()/2)
backgnd_height = (self.backgnd.height()/2)
self.canvas.create_image(backgnd_width,backgnd_height,image=self.backgnd)
# button to remove things on the canvas
button_del = ttk.Button(self.root, text='Del')
button_del['command'] = self.rem
button_del.place(x=100, y=250)
def cloud(self):
# add 5 object at random position on the canvas
for idx in range(5):
pos_x = randint(1,220)
pos_y = randint(1,220)
self.mylist.append(self.canvas.create_image(pos_x,pos_y, image=self.mycloud))
def rem(self):
# delete elements placed on the canvas
self.canvas.delete(self.mylist[-1])
self.mylist.pop()
app = App()
app.cloud()
app.root.mainloop()
made a few changes to make above code compatible with python 2:
from Tkinter import *
from PIL import Image, ImageTk
import ttk
from random import *
class App(object):
def __init__(self):
self.root = Tk()
self.canvas = Canvas(self.root, height=300, width=227)
self.canvas.grid()
self.mylist=[]
self.backgnd = ImageTk.PhotoImage( Image.open("sunshine.jpg") )
self.mycloud = ImageTk.PhotoImage( Image.open("Y.png") )
backgnd_width = (self.backgnd.width()/2)
backgnd_height = (self.backgnd.height()/2)
self.canvas.create_image(backgnd_width,backgnd_height,image=self.backgnd)
# button to remove things on the canvas
button_del = ttk.Button(self.root, text='Del')
button_del['command'] = self.rem
button_del.place(x=100, y=250)
def cloud(self):
# add 5 object at random position on the canvas
for idx in range(5):
pos_x = randint(1,220)
pos_y = randint(1,220)
self.mylist.append(self.canvas.create_image(pos_x,pos_y, image=self.mycloud))
def rem(self):
# delete elements placed on the canvas
self.canvas.delete(self.mylist[-1])
self.mylist.pop()
app = App()
app.cloud()
app.root.mainloop()

Resources