Search code examples
pythontkinterlistdirtkinter.optionmenu

Python tkinter cut down path but open as full directory


Here is my full Python code:

from tkinter import *
import glob
import os
from PIL import Image, ImageTk, ImageGrab
import tkinter as tk
import pyautogui
import datetime

#date & time
now = datetime.datetime.now()

root = tk.Tk()
root.title("SIGN OFF")
root.minsize(840, 800)

# Add a grid
mainframe = tk.Frame(root)
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)
mainframe.pack(pady=100, padx=100)

# Create a Tkinter variable
tkvar = tk.StringVar(root)


# Directory
directory = "C:/Users/eduards/Desktop/work/data/to-do"
choices = glob.glob(os.path.join(directory, "*.jpg"))
tkvar.set('...To Sign Off...') # set the default option

# Dropdown menu
popupMenu = tk.OptionMenu(mainframe, tkvar, *choices)
tk.Label(mainframe, text="Choose your sign off here:").grid(row=1, column=1)
popupMenu.grid(row=2, column=1)

label2 = tk.Label(mainframe, image=None)
label2.grid(row = 4, column = 1, rowspan = 10)

# On change dropdown callback.
def change_dropdown(*args):
    """ Updates label2 image. """
    imgpath = tkvar.get()
    img = Image.open(imgpath)
    img = img.resize((240,250))
    photo = ImageTk.PhotoImage(img)
    label2.image = photo
    label2.configure(image=photo)


tk.Button(mainframe, text="Open", command=change_dropdown).grid(row=3, column=1)


def var_states():
    text_file = open("logfile.txt", "a")
    text_file.write("TIME: %s, USER: %s, One %d, Two %d\n" % (now,os.getlogin(), var1.get(), var2.get()))
    text_file.close()
    print("One %d, Two %d" % (var1.get(), var2.get()))

var1 = IntVar()
Checkbutton(mainframe, text="Ingredients present in full (any allergens in bold with allergen warning if necessary)", variable=var1).grid(column = 2, row=1, sticky=W)
var2 = IntVar()
Checkbutton(mainframe, text="May Contain Statement.", variable=var2).grid(column = 2, row=2, sticky=W)
var3 = IntVar()
Checkbutton(mainframe, text="Cocoa Content (%).", variable=var3).grid(column = 2, row=3, sticky=W)
var4 = IntVar()
Checkbutton(mainframe, text="Vegetable fat in addition to Cocoa butter", variable=var4).grid(column = 2, row=4, sticky=W)
var5 = IntVar()
Checkbutton(mainframe, text="Instructions for Use.", variable=var5).grid(column = 2, row=5, sticky=W)
var6 = IntVar()
Checkbutton(mainframe, text="Additional warning statements (pitt/stone, hyperactivity etc)", variable=var6).grid(column = 2, row=6, sticky=W)
var7 = IntVar()
Checkbutton(mainframe, text="Nutritional Information Visible", variable=var7).grid(column = 2, row=7, sticky=W)
var8 = IntVar()
Checkbutton(mainframe, text="Storage Conditions", variable=var8).grid(column = 2, row=8, sticky=W)
var9 = IntVar()
Checkbutton(mainframe, text="Best Before & Batch Information", variable=var9).grid(column = 2, row=9, sticky=W)
var10 = IntVar()
Checkbutton(mainframe, text="Net Weight & Correct Font Size.", variable=var10).grid(column = 2, row=10, sticky=W)
var11 = IntVar()
Checkbutton(mainframe, text="Barcode - Inner", variable=var11).grid(column = 2, row=11, sticky=W)
var12 = IntVar()
Checkbutton(mainframe, text="Address & contact details correct", variable=var12).grid(column = 2, row=12, sticky=W)

def user():
    user_input = os.getlogin()
    tk.Label(mainframe, text = user_input, font='Helvetica 18 bold').grid(row = 0, column = 1)


user()


def save():
    # pyautogui.press('alt')
    # pyautogui.press('printscreen')
    # img = ImageGrab.grabclipboard()
    # img.save('paste.jpg', 'JPEG')

    var_states()


tk.Button(mainframe, text = "Save", command = save).grid(row = 20, column = 1)


root.mainloop()

When I run the code, there will be a dropdown of jpg files. Currently It shows the full directory like so:

enter image description here

I have created a post earlier on how to trim down the path and got something like this:

files = os.listdir("C:/Users/eduards/Desktop/work/data/to-do")
print(files)

But If I use that code above, it will not open the path when clicked open because it doesn't have the full path name.

What I am trying to do is, cut down the path name for display purposes and open the image by following the original full path.

As an example:

The current drop-down menu shows C:/Users/eduards/Desktop/work/data/to-do/img1.jpg My desired result is img1.jpg but in the background open the whole path of above.

Copy comment: this is what I have tried

directory = os.path.splitdrive("C:/Users/eduards/Desktop/work/data/to-do")
choices = glob.glob(os.path.join(directory[1:], "*.jpg"))

, but says

expected str, bytes or os.Pathlike, not tuple. 

Have added [1:] because the path is split into 2 and returning the 2nd part of it.


Solution

  • Question: Show only the filename in OptionMenu but get original full path from selection.

    Create your own OptionMenu which holds from all images the full path in a dict and shows only the filename as options.


    1. Define your own widget FileNameOptionMenu by inheriting from (tk.OptionMenu)
      class FileNameOptionMenu(tk.OptionMenu):
          def __init__(self, parent, directory, extension, callback):
      
    2. Get from all images the full path and extract the filename.
      Save every full path in a dict using the filename as key and the full path as value.
              # Save result from `glob` in a `dict`
              self.glob = {}
              for fpath in glob.glob(os.path.join(directory, "*.{}".format(extension))):
                  filename, extension = os.path.splitext(os.path.split(fpath)[1])
                  self.glob[filename] = fpath
      
    3. Define a variable which holds the selected option for later usage.
      Init the inherited tk.OptionMenu with the list of the keys from the dict.
      Pass the class method self.command as command=.
      Save the callback for later usage.

              self.selected = tk.StringVar(parent, 'Select a image...')
              super().__init__(parent, self.selected, *list(self.glob),
                               command=self.command)
      
              self.callback = callback
      
      
    4. This class method get called on every click option selection. On call, it calls the self.callback, which is ImageLabel.configure, with the full path of the selected option.
          def command(self, val):
              self.callback(image=self.glob.get(self.selected.get()))
      
    5. Define your own widget ImageLabel by inheriting from (tk.Label).
      This class extends the tk.Label.configure to handle .configure(image=<full path> instead of .configure(image=<image object>.
      class ImageLabel(tk.Label):
          def __init__(self, parent):
              super().__init__(parent, image=None)
      
    6. Overload the inherited class method tk.Label.configure.
      Catch the name argument image= and replace the passed full path with a image object.

          def configure(self, **kwargs):
              key = 'image'
              if key in kwargs:
                  # Replace the filepath with the image
                  fpath = kwargs[key]
      
                  img = Image.open(fpath)
                  img = img.resize((240, 250))
                  self._image = ImageTk.PhotoImage(img)
      
                  kwargs[key] = self._image
      
    7. Call the original tk.Label.configure to show the image
              super().configure(**kwargs)
      

    Usage:

    import tkinter as tk
    from PIL import Image, ImageTk
    import glob, os
    
    class App(tk.Tk):
        def __init__(self):
            super().__init__()
    
            self.label_image = ImageLabel(parent=self)
            self.label_image.grid(row=2, column=0)
    
            self.option_menu = \
                FileNameOptionMenu(parent=self,
                                   directory='C:/Users/eduards/Desktop/work/data/to-do',
                                   extension='jpg',
                                   callback=self.label_image.configure
                                                   )
            self.option_menu.grid(row=0, column=0)
    
    
    if __name__ == "__main__":
        App().mainloop()
    

    Tested with Python: 3.5