Search code examples
pythontkintercanvasraspberry-piresolution

Scaling a tkinter project to fit 320x240 raspberry Pi screen


I am working on a tkinter GUI which utilises a canvas widget in order to have an image in the background, and widgets above.This GUI will run on a 320x240 raspberry pi screen.I am new to designing GUIs for these screens, having only developed for laptops in the past. Currently the GUI looks like this:

Actual Outcome too small

As you can see, it is far too small. What I want is:

Expected Outcome

enter image description here

The reason I have made the geometry 320x240 is because I want to run this GUI on my Raspberry Pi Screen which is 320x240. The pi however mirrors the output of the HDMI cable to the screen. The HDMI outputs 1280x480. I just need it too look sharp on the raspberry pi screen, it doesn't matter how stretched it looks on the HDMI output.

Code

#!/usr/bin/env python
try:
    import Tkinter as tk
except:
    import tkinter as tk
from PIL import Image, ImageTk


root = tk.Tk()
root.attributes('-fullscreen', True)
root.geometry("1280x480")

#Define Canvas
canvas = tk.Canvas(root, width=320, height=240)
canvas.grid(row=1,column=1)


# translates an rgb tuple of int to a tkinter friendly color code
def _from_rgb(rgb):
    return "#%02x%02x%02x" % rgb

# Called when user presses View Log button
def viewLogRaise():
    #Hide Previous Windows
    canvas.itemconfigure(logButtonWindow, state="hidden")
    canvas.itemconfigure(titleLabelWindow, state="hidden")
    #Open Closed Windows
    canvas.itemconfigure(backButtonWindow, state="normal")
    canvas.itemconfigure(logTextWindow, state="normal")
    quote = """HAMLET: To be, or not to be--that is the question:
    Whether 'tis nobler in the mind to suffer
    The slings and arrows of outrageous fortune
    Or to take arms against a sea of troubles
    And by opposing end them. To die, to sleep--
    No more--and by a sleep to say we end
    The heartache, and the thousand natural shocks
    That flesh is heir to. 'Tis a consummation
    Devoutly to be wished."""
    logText.insert(tk.END, quote)

def backToMenu():
    #Hide Previous Windows
    canvas.itemconfigure(backButtonWindow, state="hidden")
    canvas.itemconfigure(logTextWindow, state="hidden")
    #Open Closed Windows
    canvas.itemconfigure(logButtonWindow, state="normal")
    canvas.itemconfigure(titleLabelWindow, state="normal")

# Background
pathToGif = "redpoly2.jpg"
# red_background=Image.open("redBackground.gif")
backgroundImage = ImageTk.PhotoImage(file=pathToGif)
canvas.background = backgroundImage
bg = canvas.create_image(0, 0, anchor=tk.NW, image=backgroundImage)
titleLabel = tk.Label(root,fg="white", text="TEXT",borderwidth=2,relief="solid", bg=_from_rgb((239, 36, 37)), font=("Courier", 44))
titleLabelWindow = canvas.create_window(160,90,window=titleLabel)
logButton = tk.Button(root,fg="white",text="View Log",command=viewLogRaise,borderwidth=2,relief="raised",bg=_from_rgb((239, 36, 37)), font=("Courier", 22))
logButtonWindow = canvas.create_window(160,180,window=logButton)
backButton = tk.Button(root,fg="white",text="Back",command=backToMenu,borderwidth=2,relief="raised",bg=_from_rgb((239, 36, 37)))
backButtonWindow = canvas.create_window(20,227,window=backButton)
canvas.itemconfigure(backButtonWindow, state="hidden")
logText=tk.Text(root,bg="white",height=12,width=35,borderwidth=2,relief="solid")
logTextWindow = canvas.create_window(160,110,window=logText)
canvas.itemconfigure(logTextWindow, state="hidden")
root.mainloop()

What I tried

I used root.attributes('-fullscreen', True), thinking this would scale the contents of the root frame to match the screens resolution, however this line only makes the tkinter window full size.

I thought about resizing the whole GUI to run on 1280x480 however this would mean that their would be too many pixels for the pi screen to show.

redpoly2 image

enter image description here


Solution

  • You can have a background image without using a Canvas widget and doing so will allow you to use tkinter's geometry managers to place your widgets. I don't really understand the relationship between the Raspberry Pi's 320x240 screen and the 1280x480 HDMI one.

    The code below illustrates how to display a background image and some widgets on top of it. There's also a Button to toggle the window's size between the two you want.

    from PIL import Image, ImageTk
    try:
        import Tkinter as tk
    except:
        import tkinter as tk
    
    
    path_to_bkgr_img = "redpoly2.jpg"
    WIN_SIZES = (320, 240), (1280, 480)
    
    
    # Translates an rgb tuple of int to a tkinter friendly color code.
    def _from_rgb(rgb):
        return "#%02x%02x%02x" % rgb
    
    def change_size():
        """ Sets/changes window size to next one available in WIN_SIZES. """
        global cur_size
        cur_size = (cur_size + 1) % len(WIN_SIZES)
        config_window()
    
    def config_window():
        """ Sets root window's title, size, and background image. """
        global background_label
    
        geometry = '{}x{}'.format(*WIN_SIZES[cur_size])
        root.geometry(geometry)
        root.title(geometry)
    
        # Resize background to fit window size.
        btn_img = background_image.resize(WIN_SIZES[cur_size], resample=Image.BICUBIC)
        btn_img = ImageTk.PhotoImage(btn_img)  # Make tkinter compatible.
    
        if not background_label:  # Create Label if necessary.
            background_label = tk.Label(root)
        background_label.config(image=btn_img)
        background_label.image = btn_img  # Keep reference.
        background_label.place(x=0, y=0, relwidth=1, relheight=1)
    
    
    root = tk.Tk()
    background_image = Image.open(path_to_bkgr_img)
    background_label = None
    cur_size = 0
    config_window()
    
    titleLabel = tk.Label(root, fg="white", text="TEXT", borderwidth=2, relief="solid",
                          bg=_from_rgb((239, 36, 37)), font=("Courier", 44))
    titleLabel.pack(padx=5, pady=5, expand=1)
    
    logButton = tk.Button(root, fg="white", text="Change Size", command=change_size,
                          borderwidth=2, relief="raised", bg=_from_rgb((239, 36, 37)),
                          font=("Courier", 22))
    logButton.pack(padx=5, pady=5, expand=1)
    
    root.bind_all('<KeyPress-Escape>', lambda *event: quit())  # Press Esc key to quit app.
    root.mainloop()
    

    Here are screenshots showing the what's displayed for each size:

    screenshot of small window

    screenshot of large window