I am writing a python script to display images, play music, and show a video on a raspberry pi when inputs are triggered. While i was working on this project I decided I wanted to add a webcam into the script. I played around with different webcam streamers until I found MPlayer which seemed to have the fastest frame rate and used the least resources. problem was, the MPlayer gui was hidden behind the tkinter windows that I was using to display the images. I tried several different things to bring the MPlayer window to the front and to make the tkinter windows go away but nothing seemed to work. Here's my code:
import sys
import os
import time
import subprocess
import RPi.GPIO as GPIO
if sys.version_info[0] == 2:
import Tkinter
tkinter = Tkinter
else:
import tkinter
from PIL import Image, ImageTk
import board
import neopixel
x=1
GPIO.setmode(GPIO.BCM)
pixels = neopixel.NeoPixel(board.D10, 38)
pixels.fill((0, 0, 0))
GPIO.setwarnings(False)
GPIO.setup(17,GPIO.OUT)
GPIO.setup(18,GPIO.OUT)
GPIO.setup(27,GPIO.OUT)
GPIO.setup(22,GPIO.OUT)
GPIO.setup(24,GPIO.OUT)
GPIO.setup(25,GPIO.OUT)
GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(4, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(5, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.output(17,GPIO.LOW)
GPIO.output(18,GPIO.LOW)
GPIO.output(27,GPIO.LOW)
GPIO.output(22,GPIO.LOW)
GPIO.output(24,GPIO.LOW)
GPIO.output(25,GPIO.LOW)
def showPIL(pilImage, exVar = 0):
try:
root.withdraw()
root.destroy()
root.update()
except:
pass
root = tkinter.Toplevel()
if x == 1:
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
else:
w, h = 100, 100
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.update()
showPIL(Image.open("Data/blank.png"))
while not GPIO.input(4):
pass
music = subprocess.Popen(['cvlc', '/home/pi/Desktop/Data/music.mp3'])
showPIL(Image.open("Data/trophy.png"))
time.sleep(1)
GPIO.output(22,GPIO.HIGH)
time.sleep(0.5)
GPIO.output(27,GPIO.HIGH)
time.sleep(0.5)
GPIO.output(18,GPIO.HIGH)
time.sleep(0.5)
GPIO.output(17,GPIO.HIGH)
time.sleep(1)
showPIL(Image.open("Data/poison.png"))
pixels.fill((0, 255, 0))
os.system("pkill tk")
x=0
showPIL(Image.open("Data/blank.png"))
x=1
camera = subprocess.Popen(['mplayer', '-fs', 'tv://'])
os.system("wmctrl -a MPlayer")
time.sleep(8)
camera.kill()
os.system("omxplayer -b '/home/pi/Desktop/Data/movie.mp4'")
showPIL(Image.open("Data/gun.png"))
GPIO.output(24,GPIO.HIGH)
GPIO.output(25,GPIO.HIGH)
while not GPIO.input(23):
pass
pixels.fill((0, 0, 0))
showPIL(Image.open("Data/dumbell.png"))
time.sleep(1)
showPIL(Image.open("Data/pipe.png"))
time.sleep(1)
showPIL(Image.open("Data/noose.png"))
time.sleep(1)
music.kill()
showPIL(Image.open("Data/blank.png"))
end = subprocess.Popen(['cvlc', '/home/pi/Desktop/Data/end.wav'])
time.sleep(8)
end.kill()
Ok so there are a few key problems here.
One massive problem is your try/except statement.
Your try except is always going to do pass. There is never going to be a time where you can destroy() something and then call update() on it. This will always result in an error and therefor the except statement of pass will run.
Next root = tkinter.Toplevel() is a problem. Because you never define the tkinter instance or what root should be you create a toplevel window instead and this will result in an instance of tkinter being opened but without a variable name to work with. That said root here is only defined locally to the function and thus any time the function is called again there is not record of root for the function to try to destroy because it has not been created yet as far as the function knows. You will need to define your root as a global variable for something like this.
Even if this works for you there should be 2 problems. One is an extra blank window showing up and 2 is that window not closing on its own when you close the toplevel window.
Next you are trying to use sleep while also running a tkinter instance. These things are not compatible without the use of threading so you need to either work threading into this or preferable learn how to use after(). After() is how tkinter manages timed events.
Instead of using Toplevel() here you needto be using Tk(). Instead of destroying and rebuilding your GUI each update you should just update your window instead. Going on the scope of what you are attempting to do you should probably play around a bit more with tkinter and learn how the event manager works before trying the raspberry pi project. Once you have a sold grasp on the Tkinter GUI and its event based process you will be able to do the more complicated stuff easier.
Related
I have a doubt, how can I continue running the script without closing a messagebox from tkinter? Or show multiple messageboxes at the same time
An example:
from tkinter import messagebox as MB
aux = 0
def scrpt():
global aux
#the script stops here ⬇️
MB.showinfo(title="Simple Program", message="aux= "+str(aux))
#the code below isnt ejecuted if i didnt accept or close the above popup ⬆️
aux += 1
while True:
scrpt()
I want to know if there is a way to continue running the script without closing the tkinter messagebox popup.
I don't think that is possible with message box because I tried it with many ways but in vain, however …
you can do it by using Toplevel() class:
from tkinter import *
for i in range(0, 100):
a = Toplevel()
Label(a, text=str(i)).pack()
mainloop()
The problem is by closing one window all will close too, you can do this so it won't close together:
for i in range(0,100):
a= Tk()
mainloop()
Why does in the code below button1 hang until the time.sleep(10) has completed.
I can only assume tKinter is waiting for the click event to finish before updating it's paint function.
I want on button1 click the state to change to DISABLED as in the code straight away, not when mainformbutton1press() has finished.
I have put time.sleep(10) to mimic rest of code functions - but the actual programme will be many minutes instead.
EDIT! - sleep is just there to show how tkinter hangs. My real programme has lots more code and no sleep function - and it takes a long time to process data with the hung GUI as mentioned. No more sleep suggestions please :)
import tkinter as tk
from tkinter import ttk
from tkinter.constants import DISABLED, NORMAL
import time
# ==================================================
class App:
def __init__(self, tk, my_w):
self.button1 = tk.Button(my_w, text="START", width=34, command = self.mainformbutton1press)
self.button1.grid(columnspan=3, row=6, column=1,padx=10,pady=20, ipadx=20, ipady=20)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def mainformbutton1press(self):
self.button1.config(text="PLEASE WAIT...")
self.button1['state'] = DISABLED
# DO REST OF PROCESSING
# TO MIMIC THIS:
time.sleep(10)
print("doing...")
# ==================================================
if __name__ == "__main__":
my_w = tk.Tk()
my_w.geometry("430x380")
my_w.resizable(False, False)
app = App(tk, my_w)
my_w.mainloop() # Keep the window open
Tk.mainloop is a sort of while loop. time.sleep() stops the loop for a particular period of time. That makes the window unresponsive. You might use .after function:
class App:
def __init__(self, tk, my_w):
self.my_w=my_w
....
def continue_next(self):
print("Doing")
....
def mainformbutton1press(self):
self.button1.config(text="PLEASE WAIT...")
self.button1['state'] = DISABLED
# DO REST OF PROCESSING
# TO MIMIC THIS:
self.my_w.after(10000,self.continue_next)
The only change you need to make to your code is to insert an update to your button.
The 10 second delay might need to be shortened (10 seconds is a long time to wait)
self.button1.config(text="PLEASE WAIT...")
self.button1['state'] = DISABLED
# INSERT UPDATE HERE
self.button1.update()
# DO REST OF PROCESSING
# TO MIMIC THIS:
time.sleep(1)
print("doing...")
I'm trying to show and hide a png (With no frame, margin, or title bar) but can't find a good way to do it. I tried using matplotlib but the code has to be in the main thread so I can't figure out how to use functions to show or hide it. I also tried tkinter but ran into the same problem where calling it from a function the code would just stop when trying to call it from a function.
Is there a simple way to accomplish this?
Below is what I am trying to accomplish though the code doesn't display any image. The code doesn't return an error either, it just stops executing at mainloop without displaying anything unless the code is in the main thread.
#Import modules
import os #For working directory
from pynput import keyboard #For hotkeys
from tkinter import *
from PIL import ImageTk,Image
#Set default directory
os.chdir('C:\\Users\\Username\\Desktop\\Python')
#Define global variables
global img
#Functions
def ExitProgram():
print('test')
#sys.exit(0)
quit() #Will not work in production, switch to sys.exit
return
def ShowImage():
root = Tk()
canvas = Canvas(root, width=300, height=300)
canvas.pack()
img = ImageTk.PhotoImage(Image.open("menu.png"))
canvas.create_image(20, 20, anchor=NW, image=img)
root.overrideredirect(True) # removes title bar
root.mainloop()
print('test')
return
#Define hotkeys
with keyboard.GlobalHotKeys({
'<ctrl>+q': ExitProgram,
'<ctrl>+0': ShowImage}) as h:
h.join()
I am trying to write a program where i have removed the main window close options and providing a exit button to the user to close the program.
After pressing i need to do some processing in the background which would be time consuming, i don't want user to close the program while that is going on accidentally. Is there a way to remove all buttons from the messagebox which is presented ?
import tkinter as tk
from win32api import GetSystemMetrics
from tkinter import messagebox
def on_closing():
pass
def exit():
messagebox.showinfo("Wait", "Please wait for background process to complete")
root.destroy()
root = tk.Tk()
root.resizable(width=False, height=False)
root.protocol("WM_DELETE_WINDOW", on_closing)
width = GetSystemMetrics(0)
height = GetSystemMetrics(1)
root.geometry('{}x{}'.format(width,height))
exitButton = tk.Button(root,text="Exit",width=15,command=exit)
exitButton.grid(row=0,column=1,padx=6,pady=6)
root.overrideredirect(True)
root.mainloop()
In the Background : There are some files generated on user's machine and i would like to archive them using python library. The files can go maybe sometime at 1GB so i think it would take more amount of time, if the laptop on which it is run is having very less computing power. And this would be the case for my base hence i want them just to wait until that popup is closed. This i can define in user manual.
I am not sure what work you want to do, but for this example I'm doing a work of printing something and then sleeping and then printing it. So this takes about 20 seconds. And in those 20 seconds you wont be able to exit the GUI.
import tkinter as tk
from win32api import GetSystemMetrics
from tkinter import messagebox
import time
import threading
def on_closing():
if started == False: #if work is not going on, then quit
root.destroy()
else: # else show the message.
messagebox.showinfo("Wait", "Please wait for background process to complete")
def work():
global started
started = True #mentioning that the work started
print('Hey')
time.sleep(5)
print('There')
time.sleep(5)
print('Whats Up')
time.sleep(5)
print('Cool?')
time.sleep(5)
started = False #mentioning that the work stopped
started = False #initially work is not started
root = tk.Tk()
root.resizable(width=False, height=False)
root.protocol("WM_DELETE_WINDOW", on_closing)
width = GetSystemMetrics(0)
height = GetSystemMetrics(1)
root.geometry('{}x{}'.format(width,height))
exitButton = tk.Button(root,text="Exit",width=15,command=on_closing)
exitButton.grid(row=0,column=1,padx=6,pady=6)
Button = tk.Button(root,text="Work",width=15,command=threading.Thread(target=work).start)
Button.grid(row=1,column=1,padx=6,pady=6)
# root.overrideredirect(True)
root.mainloop()
Here, started acts like a flag. You have to set it to True before starting your work and set it to False after it ends.
You can ignore the fact that I created a new button and used threading, it was just to simulate to you an example of work done. Threading helps the GUI to not freeze. Though I'm not sure if this will work with root.overrideredirect(True), but I think you can get rid of it.
I have some trouble at showing Tkinter + PIL image on the Windows Task Scheduler. It is works flawlessly when i compile using CMD, but the image won't appear on Task Scheduler.
Here is some of my simple code:
import Tkinter as tk
import time
from PIL import ImageTk, Image
def close():
global root
root.quit()
return;
root = tk.Tk()
img = ImageTk.PhotoImage(Image.open('C:\xxx\xxx\Desktop\RedAlert.jpg'))
def alert():
global img
panel = tk.Label(root, image = img, height=2000, width=2000)
panel.pack(side = "bottom", fill = "both", expand = "yes")
root.after(10*500, root.destroy)
root.mainloop()
count = 0
while(count < 3):
count = count+1
time.sleep(1)
if count == 2:
alert()
print count
I am using Windows 10. I also have tried to some Wav Sound with this code and some blink LED using arduino by serial. Everything works fine either on CMD and Windows Task Scheduler, except for the Image it's never pop up on The Task Sch.. I have tried to save in pyw, Bat, but still not working. Please Help me guys. :(