1

I'm on Raspberry Pi 3B+ (Raspbian) and I'm trying to load a Python script on boot which display 720p images into full screen mode, the user should then be able to press a push button to go to the next image.

Currently, I have created the code which allows for the images to display and the images to change due to the push button.

Right now I'm having trouble with having the images load into full screen mode and boot on start-up. I have followed many guides and tutorials on how I can load a GUI Python script on boot but none of them have seemed to work for my specific script (but they loaded on boot successfully from their example scripts) which is making me think that there's an issue in the code for why it won't boot (probably because I haven't implemented so that they open in full screen). These are all the guides which I tried out (https://www.raspberrypi-spy.co.uk/2015/02/how-to-autorun-a-python-script-on-raspberry-pi-boot/ , raspberry pi : Auto run GUI on boot , https://raspberrypi.stackexchange.com/questions/4123/running-a-python-script-at-startup , https://www.digikey.ca/en/maker/blogs/2018/how-to-run-python-programs-on-a-raspberry-pi , https://www.instructables.com/Raspberry-Pi-Launch-Python-script-on-startup/ , https://bc-robotics.com/tutorials/running-python-program-boot-raspberry-pi/) and I'd like to focus on the GUI method from this tutorial for my question (https://learn.sparkfun.com/tutorials/how-to-run-a-raspberry-pi-program-on-startup/all )

I'm very much a beginner right now when it comes to GUI programming (I took the original script from online and modified it to include the push button) and I'm not too sure on how I can implement the full screen option into the code which displays the images.

Here is what I have currently (will load images into a window, you can then change to the next image using a push button). At the beginning of my code, I tried adding in some functions from the example listed later which allowed toggling of full screen and also their associated root functions at the bottom to complement this

import tkinter as tk
from PIL import Image, ImageTk
import time
import sys
import os
import keyboard
import RPi.GPIO as GPIO
import time

    
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(10, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.add_event_detect(10, GPIO.FALLING) 

def toggle_fullscreen(event=None):    # I added this in to try full screen

    global root
    global fullscreen

    # Toggle between fullscreen and windowed modes
    fullscreen = not fullscreen
    root.attributes('-fullscreen', fullscreen)
    resize()

# Return to windowed mode
def end_fullscreen(event=None):  # I added this in to try full screen

    global root
    global fullscreen

    # Turn off fullscreen mode
    fullscreen = False
    root.attributes('-fullscreen', False)

root= tk.Tk()  # I added this in to try full screen
class HiddenRoot(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        #hackish way, essentially makes root window
        #as small as possible but still "focused"
        #enabling us to use the binding on <esc>
        self.wm_geometry("0x0+0+0")

        self.window = MySlideShow(self)
        #self.window.changeImage()
        self.window.startSlideShow()


class MySlideShow(tk.Toplevel):
    def __init__(self, *args, **kwargs):
        tk.Toplevel.__init__(self, *args, **kwargs)
        #remove window decorations 
        self.overrideredirect(True)

        #save reference to photo so that garbage collection
        #does not clear image variable in show_image()
        self.persistent_image = None
        self.imageList = []
        self.pixNum = 0

        #used to display as background image
        self.label = tk.Label(self)
        self.label.pack(side="top", fill="both", expand=True)

        self.getImages()
        
        #### Added the GPIO event here
        GPIO.add_event_callback(10, self.changeImage)
        
    def getImages(self):
        '''
        Get image directory from command line or use current directory
        '''
        if len(sys.argv) == 2:
            curr_dir = sys.argv[1]
        else:
            curr_dir = '.'

        for root, dirs, files in os.walk(curr_dir):
            for f in files:
                if f.endswith(".png") or f.endswith(".jpg"):
                    img_path = os.path.join(root, f)
                    print(img_path)
                    self.imageList.append(img_path)

   
    def startSlideShow(self, delay=4):
        myimage = self.imageList[self.pixNum]
        self.pixNum = (self.pixNum + 1) % len(self.imageList)
        self.showImage(myimage)
        #self.after(delay*1000, self.startSlideShow)
     #   while True:
      #      buttonState = GPIO.input(buttonPin)
       #     if buttonState == False:
        #        self.startSlideShow(self, delay=4)
    #@staticmethod
    def changeImage(self, pull_up_down=GPIO.PUD_DOWN):
        prev_input = 0
        while True:
          #take a reading
          input = GPIO.input(10)
          #if the last reading was low and this one high, print
          if ((not prev_input) and input):
            myimage = self.imageList[self.pixNum]
            self.pixNum = (self.pixNum + 1) % len(self.imageList)
            self.showImage(myimage)
          #update previous input
          prev_input = input
          #slight pause to debounce
          time.sleep(0.05)
   #     myimage = self.imageList[self.pixNum]
    #    self.pixNum = (self.pixNum + 1) % len(self.imageList)
     #   self.showImage(myimage)
        

    def showImage(self, filename):
        image = Image.open(filename)  

        img_w, img_h = image.size
        scr_w, scr_h = self.winfo_screenwidth(), self.winfo_screenheight()
        width, height = min(scr_w, img_w), min(scr_h, img_h)
        image.thumbnail((width, height), Image.ANTIALIAS)

        #set window size after scaling the original image up/down to fit screen
        #removes the border on the image
        scaled_w, scaled_h = image.size
        self.wm_geometry("{}x{}+{}+{}".format(scaled_w,scaled_h,0,0))
        
        # create new image 
        self.persistent_image = ImageTk.PhotoImage(image)
        self.label.configure(image=self.persistent_image)


slideShow = HiddenRoot()
slideShow.bind("<Escape>", lambda e: slideShow.destroy())  # exit on esc
slideShow.mainloop()
root.bind('<F11>', toggle_fullscreen)              # I added this in to try full screen
root.bind('<Escape>', end_fullscreen)              # I added this in to try full screen
toggle_fullscreen()                                # I added this in to try full screen
root.mainloop()                                    # I added this in to try full screen
GPIO.cleanup()                 

And from that guide I said to note earlier, here is the code they which loads a clock into fullscreen mode (and I followed their method of loading the script on boot which worked)

import tkinter as tk
import tkinter.font as tkFont
import time

###############################################################################
# Parameters and global variables

# Default font size
font_size = -24

# Declare global variables
root = None
dfont = None
frame = None
dtime = None

# Global variable to remember if we are fullscreen or windowed
fullscreen = False

###############################################################################
# Functions

# Toggle fullscreen
def toggle_fullscreen(event=None):

    global root
    global fullscreen

    # Toggle between fullscreen and windowed modes
    fullscreen = not fullscreen
    root.attributes('-fullscreen', fullscreen)
    resize()

# Return to windowed mode
def end_fullscreen(event=None):

    global root
    global fullscreen

    # Turn off fullscreen mode
    fullscreen = False
    root.attributes('-fullscreen', False)
    resize()

# Automatically resize font size based on window size
def resize(event=None):

    global time_dfont
    global button_dfont
    global frame

    # Resize font based on frame height (minimum size of 12)
    # Use negative number for "pixels" instead of "points"
    new_size = -max(12, int((frame.winfo_height() / 2)))
    time_dfont.configure(size=new_size)
    new_size = -max(12, int((frame.winfo_height() / 30)))
    button_dfont.configure(size=new_size)


# Read values from the sensors at regular intervals
def update():

    global root
    global dtime

    # Get local time
    local_time = time.localtime()

    # Convert time to 12 hour clock
    hours = local_time.tm_hour
    if hours > 12:
        hours -= 12

    # Add leading 0s
    shours = str(hours)
    smin = str(local_time.tm_min)
    if hours < 10:
        shours = '0' + shours
    if local_time.tm_min < 10:
        smin = '0' + smin

    # Construct string out of time
    dtime.set(shours + ':' + smin)

    # Schedule the poll() function for another 500 ms from now
    root.after(500, update)

###############################################################################
# Main script

# Create the main window
root = tk.Tk()
root.title("My Clock")

# Create the main container
frame = tk.Frame(root, bg='black')

# Lay out the main container (expand to fit window)
frame.pack(fill=tk.BOTH, expand=1)

# Variables for holding temperature and light data
dtime = tk.StringVar()

# Create dynamic font for text
time_dfont = tkFont.Font(family='Courier New', size=font_size)
button_dfont = tkFont.Font(size=font_size)

# Create widgets
label_time = tk.Label(  frame, 
                        textvariable=dtime, 
                        font=time_dfont, 
                        fg='red', 
                        bg='black')
button_quit = tk.Button(frame, 
                        text="Quit", 
                        font=button_dfont, 
                        command=root.destroy,
                        borderwidth=0,
                        highlightthickness=0, 
                        fg='gray10',
                        bg='black')

# Lay out widgets in a grid in the frame
label_time.grid(row=0, column=0, padx=20, pady=20)
button_quit.grid(row=1, column=0, padx=5, pady=5, sticky=tk.E)

# Make it so that the grid cells expand out to fill window
frame.rowconfigure(0, weight=10)
frame.rowconfigure(1, weight=1)
frame.columnconfigure(0, weight=1)

# Bind F11 to toggle fullscreen and ESC to end fullscreen
root.bind('<F11>', toggle_fullscreen)
root.bind('<Escape>', end_fullscreen)

# Have the resize() function be called every time the window is resized
root.bind('<Configure>', resize)

# Schedule the poll() function to be called periodically
root.after(20, update)

# Start in fullscreen mode and run
toggle_fullscreen()
root.mainloop()

Now what I'm trying to figure out is how I can implement their method of going into a full screen display into my program

Thank you :)!

1 Answers1

0

I am not sure if I really understood your question.

I understood you want to change on certain events to full screen. If yes, you just need to change the geometry property of your root. Like for example

master.geometry("{0}x{1}+0+0".format( master.winfo_screenwidth()-pad, master.winfo_screenheight()-pad))

above code checks for screen size and reduces some distance on w and h to fit in screen. Have a look to below link.

https://stackoverflow.com/questions/7966119/display-fullscreen-mode-on-tkinter#:~:text=This%20creates%20a%20fullscreen%20window,geometry%20and%20the%20previous%20geometry.&text=You%20can%20use%20wm_attributes%20instead%20of%20attributes%20%2C%20too.

you can create a function with that call and trigger it on start up or button pressed.

Hope my guess is right and this answers helps.

Cheers

Mono Brezel
  • 133
  • 1
  • 2
  • 6