hoek rand
hoek rand

Reputation: 329

How can I create a background for my GUI with pysimplegui?

I'm trying to create a pysimplegui application to run on my raspberry pi. I managed to set a background image, but for it to work I need to open 2 windows, one for the background and one for the layout for my app (which is length x width inputs). How can I make a pysimplegui app where I add a background to the window while also being able to add a layout on top of it? The code I'm working with at the moment is:

import PySimpleGUI as sg
from io import BytesIO
from PIL import Image
import os

def image_to_data(im):
    """
    Image object to bytes object.
    : Parameters
      im - Image object
    : Return
      bytes object.
    """
    with BytesIO() as output:
        im.save(output, format="PNG")
        data = output.getvalue()
    return data

class GUI:
    def __init__(self):
        self.size = (1280, 800)
        self.background_image = os.path.join(os.getcwd(), 'background.png')
        self.im = Image.open(self.background_image)
        #background
        self.background_layout = [[sg.Image(data=image_to_data(self.im))]]
        self.window_background = sg.Window('Background', self.background_layout, no_titlebar=True, finalize=True, margins=(0, 0), element_padding=(0,0), right_click_menu=[[''], ['Exit',]])
        #background
        self.column1 = [
                [sg.Text('Fill in the length', justification='center', font='Helvetica 18')],
                [sg.Input(justification='center', key='length'), sg.Text('X', justification='center'), sg.Input(justification='center', key='width')],
                [sg.Button('OK', key='OK')],
                [sg.Button('Exit', key='Exit')]
            ]

        self.layout = [
            [sg.Column(self.column1, element_justification='center')]
        ]
        self.window = sg.Window('Title', keep_on_top=True, layout=self.layout, finalize=True, grab_anywhere=False, transparent_color=sg.theme_background_color(), no_titlebar=True)
        
        self.start()
    def start(self):
        while True:
            window, event, values = sg.read_all_windows()
            if event in (sg.WINDOW_CLOSED, 'Exit'):
                break
        self.window.close()
        self.window_background.close()

if __name__ == '__main__':
    if os.environ.get('DISPLAY','') == '':
        print('DISPLAY variable will be set to :0.0')
        os.environ.__setitem__('DISPLAY', ':0.0')
        GUI()
    else:
        print('DISPLAY variable is already set to :0.0')

This code works fine, but if I click anywhere besides the window I put in front of my window containing a background image on my raspberry pi, the window disappears in the back. My question: Is there a way to create just one window with a background image on which I can add a layout ?

EDIT: considering I have 0 experience with pysimplegui, and made some projects in the past using Kivy, I've decided to stick to Kivy. Closing this question.

Kivy example code:

main.py:

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.config import Config
Config.set('graphics', 'width', '1280')
Config.set('graphics', 'height', '800')

class MainWidget(BoxLayout):
    pass

class TheLabApp(App):
    pass

if __name__ == '__main__':
    TheLabApp().run()

thelab.kv:

#:kivy 2.1.0
MainWidget:

<MainWidget>:
    canvas.before:
        Rectangle:
            pos: self.pos
            size: self.size
            source: 'assets/background.png' #note how simple it is to add a background image
    BoxLayout:
        orientation: 'vertical'
        Label:
        BoxLayout:
            orientation: 'vertical'
            Label:
                text: 'Fill in the length'
                font_size: 30
            BoxLayout:
                orientation: 'horizontal'
                TextInput:
                    id: width
                    multiline: False
                    font_size: 30
                    #size_hint: (0.5, 1)
                Label:
                    text: 'X'
                    font_size: 20
                TextInput:
                    id: length
                    multiline: False
                    font_size: 20
        Label:

Upvotes: 1

Views: 1100

Answers (1)

Jason Yang
Jason Yang

Reputation: 13061

Here's the example, but with tkinter code.

import os
from io import BytesIO
from PIL import Image
import PySimpleGUI as sg

def image_to_data(im):

    with BytesIO() as output:
        im.save(output, format="PNG")
        data = output.getvalue()
    return data

def make_background(window, file, main_frame):

    global images

    def find_frames(widget):
        widgets = list(widget.children.values())
        if isinstance(widget, (sg.tk.Frame, sg.tk.LabelFrame)):
            widget.update()
            x, y = widget.winfo_rootx() - x0, widget.winfo_rooty() - y0
            width, height = widget.winfo_width(), widget.winfo_height()
            new_im = im_.crop((x, y, x+width, y+height))
            image = sg.tk.PhotoImage(data=image_to_data(new_im))
            images.append(image)
            label = sg.tk.Label(widget, image=image, padx=0, pady=0, bd=0, bg=bg)
            label.place(x=0, y=0)
            label.lower()
        for widget in widgets:
            find_frames(widget)

    size = window.size
    im_ = Image.open(file).resize(size)
    root = window.TKroot
    widgets = list(root.children.values())
    x0, y0 = root.winfo_rootx(), root.winfo_rooty()

    frame = sg.tk.Frame(root, padx=0, pady=0, bd=0, bg=bg)
    frame.place(x=0, y=0)
    images = []
    image = sg.tk.PhotoImage(data=image_to_data(im_))
    images.append(image)
    label = sg.tk.Label(frame, image=image, padx=0, pady=0, bd=0, bg=bg)
    label.pack()
    main_frame.Widget.master.place(in_=frame, anchor='center', relx=.5, rely=.5)
    frame.lower()
    frame.update()
    for widget in widgets:
        find_frames(widget)

bg = sg.theme_background_color()
background_image_file = os.path.join(os.getcwd(), 'background.png')
size = (640, 480)

sg.set_options(dpi_awareness=True)

frame = [
    [sg.Text('Fill in the length', justification='center', background_color='black', font='Helvetica 18')],
    [sg.Input(justification='center', size=10, key='length'),
     sg.Text('X', justification='center', background_color='black'),
     sg.Input(justification='center', size=10, key='width')],
    [sg.Button('OK', key='OK')],
    [sg.Button('Exit', key='Exit')]
]
# Need only one frame here to move it to center of window
layout = [[sg.Frame('', frame, border_width=0, key='FRAME', background_color=bg)]]

location = sg.Window.get_screen_size()
window = sg.Window('Background Demo', layout, margins=(0, 0), grab_anywhere=True,
    size=size, keep_on_top=True, finalize=True,
    no_titlebar=True,
    transparent_color=bg,
)

images = []
make_background(window, background_image_file, window['FRAME'])

while True:
    event, values = window.read()
    if event in (sg.WINDOW_CLOSED, 'Cancel', 'Exit'):
        break
    print(event)

window.close()

enter image description here

Upvotes: 1

Related Questions