1

I am creating a software that when it receives the signal of a button, displays an image and after 1 second another one is displayed. The problem is that I do not know how to close the image once it has already been shown in full screen, it is assumed that the Esc closes, but it does not work.

import sys
import RPi.GPIO as GPIO 
import time 

pulse = 16
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

GPIO.setup(pulse, GPIO.IN, GPIO.PUD_UP) 

if sys.version_info[0] == 2:  
    import Tkinter
    tkinter = Tkinter 
else:
    import tkinter
from PIL import Image, ImageTk

blackImage = Image.open("black.png")
pattern = Image.open("pattern.jpg")

def showImage(nimage):
    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 = nimage.size
    if imgWidth > w or imgHeight > h:
        ratio = min(w/imgWidth, h/imgHeight)
        imgWidth = int(imgWidth*ratio)
        imgHeight = int(imgHeight*ratio)
        nimage = nimage.resize((imgWidth,imgHeight), Image.ANTIALIAS)
    image = ImageTk.PhotoImage(nimage)
    imagesprite = canvas.create_image(w/2,h/2,image=image)
    root.mainloop()


while True:

    if GPIO.input(pulse) == False:
        time.sleep(0.1)
        print ("Shoot")
        showImage(blackImage)
        time.sleep(1)
        showImage(pattern)

The result would be that when the button is pressed, the black image will be shown on the screen and then the image of the pattern, but only the black image will appear and when the second image is not replaced by the image of the pattern, it will not be closed at the same time. press Esc, I have to press Alt + F4.

Miraj50
  • 4,257
  • 1
  • 21
  • 34
Jano CL
  • 69
  • 11

1 Answers1

1

GUI-programming is user event-driven which means rules for programming it are different from the very common function-level programming you're probably used to doing. The link in @stovfl's comment gets into the differences, so I suggest you read what it says.

To help understand how this affects the way things are done, below is an attempt to convert your code over to this paradigm. Also note that since I don't have a Raspberry Pi, the code also (conditionally) binds an event-handler callback function to left mouse-button events to simulate there being one — so feel free to remove that stuff if you wish given that you do.

I've tried to encapsulate as much as possible of what needs to be done in a single "application" class because doing so makes the coding a little cleaner by reducing the need to use a bunch of global variables.

from PIL import Image, ImageTk
try:
    import tkinter as tk  # Python 3
except ModuleNotFoundError:
    import Tkinter as tk  # Python 2

try:
    import RPi.GPIO as GPIO
except ModuleNotFoundError:
    GPIO_present = False  # No Raspberry Pi
else:
    GPIO_present = True
    GPIO_PULSE = 16  # Button channel.
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)
    GPIO.setup(GPIO_PULSE, GPIO.IN, GPIO.PUD_UP)


class Application(tk.Frame):
    DELAY = 100  # ms

    def __init__(self, master=None):
        super().__init__(master)
        self.pack()
        self.w, self.h = self.winfo_screenwidth(), self.winfo_screenheight() # Fullscreen
        self.create_widgets()
        self.flag = False  # Initialize button clicked flag.
        self.after(self.DELAY, self.check_signal)  # Start background polling.

    def create_widgets(self):
        self.canvas = tk.Canvas(root, width=self.w, height=self.h, background='black')
        self.canvas.pack()

        pil_img = Image.open("black.png")

    def _load_image(self, filename):
        """ Use PIL to load (and resize if necessary) an image. """
        pil_img = Image.open(filename)
        img_width, img_height = pil_img.size

        if img_width > self.w or img_height > self.h:  # Too big?
            ratio = min(self.w/img_width, self.h/img_height)
            img_width, img_height = int(img_width*ratio), int(img_height*ratio)
            pil_img = pil_img.resize((img_width, img_height), Image.ANTIALIAS)  # Resize.

        img = ImageTk.PhotoImage(pil_img)  # Convert to tkinter PhotoImage.
        return img

    def create_widgets(self):
        self.canvas = tk.Canvas(root, width=self.w, height=self.h, background='black')
        self.canvas.pack()

        self.black_img = self._load_image("black.png")
        self.pattern_img = self._load_image("pattern.png")

        # Create a canvas image object place-holder for show_image() to update.
        self.image_id = self.canvas.create_image(self.w/2, self.h/2, image=None)

    def show_image(self, img):
        self.cur_img = img
        self.canvas.itemconfigure(self.image_id, image=self.cur_img)

    def show_next_image(self):
        self.after(100)  # Pause 0.1 second - avoid using time.sleep()
        print("Shoot")
        self.show_image(self.black_img)
        self.after(1000)  # Pause 1 second - avoid using time.sleep()
        self.show_image(self.pattern_img)

    def update_flag(self, e):
        """ Mouse left-button clicked handler. """
        self.flag = True

    def check_signal(self):
        if GPIO_present:
            self.flag = not GPIO.input(GPIO_PULSE)
        else:
            pass  # Assume something else is keeping self.flag updated.

        if self.flag:
            self.show_next_image()
            self.flag = False  # Reset

        root.after(self.DELAY, self.check_signal)  # Check again after delay.

if __name__ == '__main__':

    root = tk.Tk()
    w, h = root.winfo_screenwidth(), root.winfo_screenheight()
    root.overrideredirect(True)
    root.geometry("%dx%d+0+0" % (w, h))  # Fullscreen
    root.focus_set()
    root.bind("<Escape>", lambda e: e.widget.quit())

    app = Application(root)

    if not GPIO_present:
        # Using left mouse-button as substitute for GPIO.
        # Bind left mouse button click event handler.
        root.bind("<Button-1>", app.update_flag)

    app.mainloop()
martineau
  • 119,623
  • 25
  • 170
  • 301
  • Good, I've been bundled and I have not seen so far, I've modified the code thanks to its super contribution (I must say that I'm new to Python and there are things that I do not fully understand yet). The problem that I am having now is when pressing the button, the software detects it and starts the execution of showImage () and fails giving the following error: – Jano CL Feb 04 '19 at 11:56
  • Exception in Tkinter callback Traceback (most recent call last):   ...   File "/usr/lib/python3.5/tkinter/_init_.py", line 608, in callit     func (* args)   File "/home/pi/Documents/OriginalStack.py", line 76, in check_signal     self.show_next_image ()   File "/home/pi/Documents/OriginalStack.py", line 63, in show_next_image     self.show_image (self.pattern_img)   File "/home/pi/Documents/OriginalStack.py", line 54, in show_image     self.cur_img = img.resize ((img_width, img_height), Image.ANTIALIAS) AttributeError: 'PhotoImage' object has no attribute 'resize' – Jano CL Feb 04 '19 at 11:57
  • And according to my tests the "Escape" button does not end the application or close the entire screen. I'll keep looking to see why this problem happens. Thank you so much for your help** – Jano CL Feb 04 '19 at 11:58
  • Sorry about the `resize` issue. I didn't notice because the images I used for testing were smaller that my computer's screen, so that execution path was never taken—see updated answer where the resizing, if necessary, is now done in a new method I added named `_load_image()`. There is no "Escape" button, but pressing the "Esc" **key** on the keyboard works for me. The `root.bind("", lambda e: e.widget.quit())` is what configures that to happen - make sure that is getting executed if that's what you meant. – martineau Feb 04 '19 at 17:28
  • Also note that a side-effect of the fixes is that the `show_image()` method no longer attempts to resize any image it is passed that too big. I originally was handling that there to make it more general-purpose because I suspected that eventually you might want to show more than just the two images the code is now hardcoded to display. If that becomes something you want to be able to do, a different set of modifications would be required. – martineau Feb 04 '19 at 17:46
  • Thank you very much, this is much better. The Esc to me does not work but I will look for another solution, I wanted to ask if you know the method to stop showing the current image and stay the background in black, as in the beginning. Thanks again – Jano CL Feb 04 '19 at 22:09
  • 1
    Now all is working, thank you very much! Have a nice day! – Jano CL Feb 22 '19 at 10:56