Search code examples
pythontkintertkinter-entryttkwidgets

Python how to use a value from another function and bind the function to a Tkinter button?


I'm new to programming in general. I have made a simple tool that converts images into WebP format.

from PIL import Image
from tkinter import Tk, PhotoImage
from tkinter import filedialog as fd
from tkinter.ttk import Label, Button
# Set up window and configure the sizes
window = Tk()
window.title('Webp Converter')
window.geometry('300x150')
window.eval('tk::PlaceWindow . center')
window.tk.call('tk', 'scaling', 1.5)
icon = PhotoImage(file="uhggg-16.png")
window.iconphoto(False, icon)
# Define functions
def uploadConvert():
    filepath = fd.askopenfilename()
    filename = filepath.split('.')[0]
    image = Image.open(filepath)
    image = image.convert('RGB')
    if image.size[0] > 700 or image.size[1] > 700:
        image.thumbnail(size=((700, 700)))
    image.save(f'{filename}.webp', 'webp')
# Labels and fields
lbl = Label(window, text='Select your image to convert:')
lbl.grid(column=0, row=0, padx=20, pady=20)
btn_upload = Button(text='Upload and convert', command=uploadConvert)
btn_upload.grid(column=0, row=1)
window.mainloop()

Everything works fine here.

I wanted to add a feature that display the selected image's size (width x height) in 2 input fields and you can change width and height, width/height ratio stays the same, or you can leave it as it is and proceed webp conversion. I know I will need an extra button and 2 input fields to do that. But I don't know what exactly to do.

I tried to split the uploadConvert() function into 2 functions: upload() and convert(). upload() opens the image and get the image name, convert(filename, image) receives 2 parameters from upload() and convert the image. upload() and convert() will be bind to 2 separate ttk buttons.

The first issue I encountered is that convert() won't get the objects from upload() and also I don't know how exactly I should bind a function to button with multiple params using "command=" for ttk buttons.

I have also tried to add 2 Entry as width and height display/input fields..

Edited Code:

from PIL import Image
from tkinter import PhotoImage, Entry
import tkinter as tk
from tkinter import filedialog as fd
from tkinter.ttk import Label, Button
# Set up window and configure the sizes
window = tk.Tk()
window.title('WEBPER')
window.geometry('230x300')
window.eval('tk::PlaceWindow . center')
window.tk.call('tk', 'scaling', 1.5)
icon = PhotoImage(file="uhggg-16.png")
window.iconphoto(False, icon)
# Define functions
def upload():
    global filepath, filename
    filepath = fd.askopenfilename()
    filename = filepath.split('.')[0]
    image = Image.open(filepath)
    wvar.set(image.size[0])
    hvar.set(image.size[1])
    lbl_bot.configure(text='Image uploaded', foreground='#000000')

def convert():
    try:
        wint = int(wvar.get())
        hint = int(hvar.get())
        image = Image.open(filepath)
        image = image.convert('RGB')
        image.thumbnail(size=((wint, hint)))
        image.save(f'{filename}.webp', 'webp')
        lbl_bot.configure(text='Succesfully converted!', foreground='#4BB543')
    except:
        lbl_bot.configure(text='Upload your image first!', foreground='#FF0000')
# Labels and fields
wvar = tk.StringVar()
wvar.set('0')
hvar = tk.StringVar()
hvar.set('0')
lbl_top = Label(window, text='Select your image to convert:')
lbl_top.grid(column=0, row=0, padx=20, pady=20)
lbl_bot = Label(window, text='No image selected', foreground='#000000', background='#D3D3D3')
lbl_bot.grid(column=0, row=10, padx=20, pady=(15,20))
lbl = Label(window, text='Width:')
lbl.grid(column=0, row=1)
lbl = Label(window, text='Height:')
lbl.grid(column=0, row=3)
width = Entry(textvariable=wvar, width=7)
width.grid(column=0, row=2)
height = Entry(textvariable=hvar, width=7)
height.grid(column=0, row=4, pady=(0, 20))
btn_upload = Button(text='Upload', command=upload)
btn_upload.grid(column=0, row=5, pady=(0, 10))
btn_convert = Button(text='Convert', command=convert)
btn_convert.grid(column=0, row=6)
window.mainloop()

Solution

  • filepath and filename are derived from the function upload() which in turn will be used by the function convert(). So declare these two variables as global so that these can be used in other functions as well.

    But convert() can use the variables of upload() only if upload() was executed before convert(). Logically, the user is going to upload file and then only will convert. So, naturally upload() will be executed. So this fulfils our requirement.

    But in case if the user clicks convert button without clicking upload button, convert will be executed. But since the variables filepath and filename are not defined, it'll give name error. To resolve this, We use 'try..except..' method to handle error.

    By importing messagebox module, user can get popup messages. So, it is coded like: if there is name error, the messagebox will popout asking the user to upload file first.

    Now as upload and convert are two different buttons which triggers two different functions viz upload() and convert() respectively, we don't need to worry about binding buttons with two functions.

    Since you wanted the user input of width and height, these can be added with Entry widgets. The user input can be received by Entry_widget.get(). Here width.get and height.get(). As these need to be converted into integers, these will be int(width.get()) and int(height.get(()).

    The Entry widget you didn't import. I added that part as well.

    Edit:

    The following part is the edited code of the previous one. I edited after seeing your comment.

    Since you want the scale to be maintained during conversion, the user is just required give the required width alone. So that our code will decide the required height accordingly.(Reqd_height=(actual_height/actual_width)*got_width)

    Value error sometime happens when we convert directly from string. (Actually it didn't happen when I ran). when we make got_width=int(float(width)), this won't occur. corrected in the code.

    works perfect,

    Just take this as a basic concept and be creative in implementing your own ideas.For eg., you can modify like: the program ask the user whether they want to enter required height or required width. If the user selects 'required width', the required height will be calculated like how we have done. If the user selects 'required_height', the program will calculate the 'required_width'.

    from PIL import Image
    from tkinter import Tk, PhotoImage, Entry
    from tkinter import filedialog as fd
    from tkinter.ttk import Label, Button
    from tkinter import messagebox as mb
    
    # Define functions
    def upload():
        global filepath
        global filename
        filepath = fd.askopenfilename()
        filename = filepath.split('.')[0]
        
    def convert():
        
       try:
            got_width = int(float(width.get()))
            image = Image.open(filepath)
            image = image.convert('RGB')
            actual_width = image.size[0]
            actual_height = image.size[1]
            if actual_width > got_width:
                reqd_height = (actual_height/actual_width)*got_width
                reqd_height = round(reqd_height,0)
            else: reqd_height = actual_height
            image.thumbnail(size=((got_width, reqd_height)))
            image.save(f'{filename}.webp', 'webp')
        except NameError:
            mb.showinfo('Upload File', 'Upload your file before converting')
            
     
    
    # Set up window and configure the sizes
    window = Tk()
    window.title('Webp Converter')
    window.geometry('300x350')
    window.eval('tk::PlaceWindow . center')
    
    
    window.tk.call('tk', 'scaling', 1.5)
    icon = PhotoImage(file="uhggg-16.png")
    window.iconphoto(False, icon)
    
        
    # Labels and fields
    wdth = Label(window, text='Required width:')
    wdth.grid(column=0, row=1, padx=20, pady=20)
    
    width = Entry(window)
    width.grid(column=0, row=2, padx=20, pady=20)
    
    lbl = Label(window, text='Select your image to convert:')
    lbl.grid(column=0, row=5, padx=20, pady=20)
    btn_upload = Button(text='Upload', command=upload)
    btn_upload.grid(column=0, row=6)
    
    btn_convert = Button(text='convert', command=convert)
    btn_convert.grid(column=0, row=7)
    
    
    window.mainloop()