如何通过Tkinter在Python中创建可选择的图像“列表”

huangapple go评论76阅读模式
英文:

How to create a selectable "list" of images via Tkinter in Python

问题

以下是您要翻译的内容:

假设有一个包含大量图片的文件夹。当用户打开窗口时,程序应加载文件夹中的所有图像,并以可选择的“ListBox”形式显示它们,带有滚动条,其中不仅包括图像,还包括它们的名称。

我已经尽力在图片1中绘制了它。
图片1

因此,程序应该像图片2中所示那样工作。
图片2

用户选择需要删除的图像,然后按下删除按钮。之后,图片会从文件夹中消失,所谓的“列表”会自动更新,显示文件夹中剩余的图片。

问题是,我不知道如何创建这个可“选择”的图像列表框。之所以称其为ListBox(在这种情况下可能不正确),是因为我设法创建了一个程序,使用ListBox小部件,仅显示文件夹中图片的名称,没有它们的图像。但是,我可以选择其中的多个并一次删除多个。

所以我的问题是,'是否可以通过Tkinter创建我所描述的这个东西?' 我只需要知道如何创建带有图像和它们名称的这种“ListBox”。

如果有人有任何关于如何做到这一点的想法或见解,请分享
我将非常感激!
提前感谢所有人!

英文:

Say there is a folder with a bunch of pictures. When a user opens the window the program should load all of the images in that are in the folder and display them in a kind of a selectable "ListBox" with ScrollBar, which contains not only images, but their names underneath as well.

I've tried my best to draw it in Picture 1.
Picture 1

So the program should work like this, as in Picture 2.
Picture 2

User selects the images that needs to be deleted and presses the delete_button. After that the pictures disappear from the folder and the so called "list" of images automatically updates displaying the remaining pictures in the folder.

The problem is that I don't know how to create this "selectable" ListBox of images. The reason why I call it ListBox (it's probably not correct in this case) is because I managed to create a program, using ListBox widget, that displays only the names of the pictures that are in the folder, without images of them though. However, I can select multiple of them and delete several in one time.

So my question is 'Is it possible to create the thing I've described via Tkinter?' I only need to know how to create such 'ListBox' with images and their names

If anybody has any ideas or insights on how to do it, please, share it
I'll be extremely grateful!
Thanks to everyone in advance!

答案1

得分: 0

这是您提供的代码的翻译部分:

# needed imports
import tkinter as tk
from PIL import Image, ImageTk
import os
from PIL.Image import Resampling
from math import ceil

# create a window
root = tk.Tk()

# set window's size
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
root.geometry("%dx%d" % (screen_width, screen_height))

# configure columns and rows
root.columnconfigure(0, weight=3)
root.columnconfigure(2, weight=1)
root.rowconfigure(0, weight=3)

# create canvas, which will hold the selectable buttons
canvas = tk.Canvas(root, bg='Black')
canvas.grid(row=0, column=0, sticky='nwes')

# create scrollbar for canvas
scroll = tk.Scrollbar(root, orient="vertical")
scroll.grid(row=0, column=1, sticky='nswe')

# create frame which will be placed into canvas
containter = tk.Frame(root, padx=10)

# create another frame which will hold a label and a button for deleting selected images
side_containter = tk.Frame(root, padx=10)
side_containter.grid(row=0, column=2)

# create label which will indicate how many images are chosen
mylable = tk.Label(side_containter, text='Chosen: ', font=(None, 20), pady=150)
mylable.pack()

# create button by pressing which the selected images will be deleted
delete_button = tk.Button(side_containter, text='Delete', font=(None, 20), command=lambda: delete())
delete_button.pack()

# make the canvas expandable
def update_size(e=None):
    canvas["scrollregion"] = canvas.bbox("all")

# create a binding on canvas
canvas.bind('<Configure>', update_size)
canvas.after_idle(update_size)

# declare the needed directory, where the images are
path = f"{os.getcwd()}\{'Acceptances'}\Acceptance1"

# create two lists, first one is going to hold image objects, second one will hold their 'labels'
image_list = [] # 1-st parameter
image_vars = [] # 2-nd parameter

# collect all the files that are in the directory (assume that there are only needed types of file like .png or .jpg
# (otherwise a fileter should be created for unwanted types of files)
get_image_files = lambda: os.listdir(path) # 3-rd parameter

# create function which takes three parameters, which I declared above.
def fill_canvas(image_list, image_vars, image_files):
    global path

    # using for loop append lists that will hold image objects and the names to them
    for image in range(0, len(image_files)):
        image_list.append(ImageTk.PhotoImage(Image.open(path + '/' + image_files[image]), Resampling.LANCZOS)) # image object
        image_vars.append(f'img_{image}') # names

    # declare how many columns do you want and calculate how many rows will be needed by dividing quantity of images in the folder on the number of columns
    columns = 5
    rows = ceil(len(image_vars) / columns)

    # declare:
    positions = []  # list which will hold the positions of images in a row like [0, 1, 2, 3 ,4]
    matrix = {} # dictionary which will be kind of a matrix, indicating what positions each row contains
    pos = 0 # variable to count the positions

    # via for loop fill the matrix
    for row in range(rows):

        for column in range(columns):
            if pos == len(image_vars):
                break
            else:
                positions.append(pos)
                pos += 1
        matrix[row] = positions
        positions = []

    # create button objects using for loop
    for key, value in matrix.items(): # take a row and the positions it contains like [row1]:[0,1,2,3,4]
        for n in range(len(image_vars)): # for each image that is in the folder
            if n in value: # if the count doesn't go beyond the quantity of the images in the folder do:
                # create a button and add it place it into the frame
                globals()[image_vars[n]] = tk.Button(containter, text=image_vars[n], font=(None, 12),
                                                     image=image_list[n], compound=tk.TOP, bg='snow',
                                                     command=lambda n=n: print_button(n))
                globals()[image_vars[n]].grid(row=key, column=value.index(n))

    # make the canvas scrollable
    containter.bind('<Configure>', lambda e: canvas.configure(scrollregion=canvas.bbox('all')))

    # place the frame with buttons inside of the canvas
    canvas.create_window((0, 0), window=containter, anchor="nw", tags='my_tag')

    # set configs on scrollbar and canvas
    scroll.config(command=canvas.yview)
    canvas.config(yscrollcommand=scroll.set)

    # set canvas scroll regions
    def set_canvas_scrollregion(event):

        width = event.width - 4
        canvas.itemconfigure("my_tag", width=width)
        canvas.config(scrollregion=canvas.bbox("all"))

    # create binding
    canvas.bind("<Configure>", lambda e: set_canvas_scrollregion)

# call the function that will display the buttons
fill_canvas(image_list, image_vars, get_image_files())

# declare an empty list which will hold the selected buttons
selected_buttons = []

# create a function which will change a background of a button which was selected and add it to the counter of selected ones
def print_button(image):
    if image not in selected_buttons: # if the pressed button is not in selected_buttons, then it'll add it and will change the background color
        selected_buttons.append(image)
        globals()[image_vars[image]].config(bg='light blue')
    else: # if it's there it will remove the button from the list and will change the color back as well
        selected_buttons.remove(image)
        globals()[image_vars[image]].config(bg='snow')

    mylable.config(text=f'Chosen: {len(selected_buttons)}') # change the counter

# create delete selected buttons function
def delete():
    global selected_buttons, get_image_files, image_list, image_vars

    image_files = get_image_files() # use our lambda function to get all files from a folder

    if not selected_buttons: # if selected_buttons list is empty
        print('nothing is selected')
    else: # if not and something was selected
        for photo in selected_buttons:
            os.remove(f'{path}\{image_files[photo]}')  # remove each image that was selected from the computer

        # to make the canvas display the remaining buttons after the deleting selected ones:
        canvas.delete("all") # clear the canvas

        selected_buttons = [] # reset selected_buttons list
        mylable.config(text='Chosen: 0') # reset counter label

        # reset those lists which will contain the remaining images and their names in the folder
        image_list = []
        image_vars = []

        # call the function that will display the remaining files in the folder
        fill_canvas(image_list, image_vars, get

<details>
<summary>英文:</summary>

It&#39;s been quite a while, but I&#39;m back now with a solution which I expected to come up with. In a couple of days after posting this question I postponed working on this idea cause the guy who initially asked me to create this program said that it&#39;s unnecessary. Though in the beginning I thought the opposite. Anyway, these days I&#39;ve been playing around with this idea in my spare time. I managed to do something sketchy, but at least it demonstrates a little bit of the program I wanted to create back then.

&gt; Before I post the pictures and the code I want to say thank you to
&gt; everyone who tried to help me! :)


**Here are some pictures:** 

This
[Picture][1] shows how some of the images are getting selected.

And this [Picture][2] shows how the selected images are being deleted.


**Here is the code I sketched:**

    # needed imports
    import tkinter as tk
    from PIL import Image, ImageTk
    import os
    from PIL.Image import Resampling
    from math import ceil
    
    # create a window
    root = tk.Tk()
    
    # set window&#39;s size
    screen_width = root.winfo_screenwidth()
    screen_height = root.winfo_screenheight()
    root.geometry(&quot;%dx%d&quot; % (screen_width, screen_height))
    
    # configure columns and rows
    root.columnconfigure(0, weight=3)
    root.columnconfigure(2, weight=1)
    root.rowconfigure(0, weight=3)
    
    # create canvas, which will hold the selectable buttons
    canvas = tk.Canvas(root, bg=&#39;Black&#39;)
    canvas.grid(row=0, column=0, sticky=&#39;nwes&#39;)
    
    # create scrollbar for canvas
    scroll = tk.Scrollbar(root, orient=&quot;vertical&quot;)
    scroll.grid(row=0, column=1, sticky=&#39;nswe&#39;)
    
    # create frame which will be placed into canvas
    containter = tk.Frame(root, padx=10)
    
    # create another frame which will hold a label and a button for deleting selected images
    side_containter = tk.Frame(root, padx=10)
    side_containter.grid(row=0, column=2)
    
    # create label which will indicate how many images are chosen
    mylable = tk.Label(side_containter, text=&#39;Chosen: &#39;, font=(None, 20), pady=150)
    mylable.pack()
    
    # create button by pressing which the selected images will be deleted
    delete_button = tk.Button(side_containter, text=&#39;Delete&#39;, font=(None, 20), command=lambda: delete())
    delete_button.pack()
    
    # make the canvas expandable
    def update_size(e=None):
        canvas[&quot;scrollregion&quot;] = canvas.bbox(&quot;all&quot;)
    
    # create a binding on canvas
    canvas.bind(&#39;&lt;Configure&gt;&#39;, update_size)
    canvas.after_idle(update_size)
    
    # declare the needed directory, where the images are
    path = f&quot;{os.getcwd()}\{&#39;Acceptances&#39;}\Acceptance1&quot;
    
    # create two lists, first one is going to hold image objects, second one will hold their &#39;labels&#39;
    image_list = [] # 1-st parameter
    image_vars = [] # 2-nd parameter
    
    # collect all the files that are in the directory (assume that there are only needed types of file like .png or .jpg
    # (otherwise a fileter should be created for unwanted types of files)
    get_image_files = lambda: os.listdir(path) # 3-rd parameter
    
    # create function which takes three parameters, which I declared above.
    def fill_canvas(image_list, image_vars, image_files):
        global path
    
        # using for loop append lists that will hold image objects and the names to them
        for image in range(0, len(image_files)):
            image_list.append(ImageTk.PhotoImage(Image.open(path + &#39;/&#39; + image_files[image]), Resampling.LANCZOS)) # image object
            image_vars.append(f&#39;img_{image}&#39;) # names
    
        # declare how many columns do you want and calculate how many rows will be needed by dividing quantity of images in the folder on the number of columns
        columns = 5
        rows = ceil(len(image_vars) / columns)
    
        # declare:
        positions = []  # list which will hold the positions of images in a row like [0, 1, 2, 3 ,4]
        matrix = {} # dictionary which will be kind of a matrix, indicating what positions each row contains
        pos = 0 # variable to count the positions
    
        # via for loop fill the matrix
        for row in range(rows):
    
            for column in range(columns):
                if pos == len(image_vars):
                    break
                else:
                    positions.append(pos)
                    pos += 1
            matrix[row] = positions
            positions = []
    
        # create button objects using for loop
        for key, value in matrix.items(): # take a row and the positions it contains like [row1]:[0,1,2,3,4]
            for n in range(len(image_vars)): # for each image that is in the folder
                if n in value: # if the count doesn&#39;t go beyond the quantity of the images in the folder do:
                    # create a button and add it place it into the frame
                    globals()[image_vars[n]] = tk.Button(containter, text=image_vars[n], font=(None, 12),
                                                         image=image_list[n], compound=tk.TOP, bg=&#39;snow&#39;,
                                                         command=lambda n=n: print_button(n))
                    globals()[image_vars[n]].grid(row=key, column=value.index(n))
    
        # make the canvas scrollable
        containter.bind(&#39;&lt;Configure&gt;&#39;, lambda e: canvas.configure(scrollregion=canvas.bbox(&#39;all&#39;)))
    
        # place the frame with buttons inside of the canvas
        canvas.create_window((0, 0), window=containter, anchor=&quot;nw&quot;, tags=&#39;my_tag&#39;)
    
        # set configs on scrollbarr and canvas
        scroll.config(command=canvas.yview)
        canvas.config(yscrollcommand=scroll.set)
    
        # set canvas scroll regions
        def set_canvas_scrollregion(event):
    
            width = event.width - 4
            canvas.itemconfigure(&quot;my_tag&quot;, width=width)
            canvas.config(scrollregion=canvas.bbox(&quot;all&quot;))
    
        # create binding
        canvas.bind(&quot;&lt;Configure&gt;&quot;, lambda e: set_canvas_scrollregion)
    
    # call the function that will display the buttons
    fill_canvas(image_list, image_vars, get_image_files())
    
    # declare an empty list which will hold the selected buttons
    selected_buttons = []
    
    # create a function which will change a background of a button which was selected and add it to the counter of selected ones
    def print_button(image):
        if image not in selected_buttons: # if the pressed button is not in selected_buttons, then it&#39;ll add it and will change the backgorund color
            selected_buttons.append(image)
            globals()[image_vars[image]].config(bg=&#39;light blue&#39;)
        else: # if it&#39;s there it will remove the button from the list and will change the color back as well
            selected_buttons.remove(image)
            globals()[image_vars[image]].config(bg=&#39;snow&#39;)
    
        mylable.config(text=f&#39;Chosen: {len(selected_buttons)}&#39;) # change the counter
    
    # create delete selected buttons function
    def delete():
        global selected_buttons, get_image_files, image_list, image_vars
    
        image_files = get_image_files() # use our lambda function to get all files from a folder
    
        if not selected_buttons: # if selected_buttons list is empty
            print(&#39;nothing is selected&#39;)
        else: # if not and something was selected
            for photo in selected_buttons:
                os.remove(f&#39;{path}\{image_files[photo]}&#39;)  # remove each image that was selected from the computer
    
            # to make the canvas display the remaining buttons after the deleting selected ones:
            canvas.delete(&quot;all&quot;) # clear the canvas
    
            selected_buttons = [] # reset selected_buttons list
            mylable.config(text=&#39;Chosen: 0&#39;) # reset counter label
    
            # reset those lists which will contain the remaining images and their names in the folder
            image_list = []
            image_vars = []
    
            # call the function that will display the remaining files in the folder
            fill_canvas(image_list, image_vars, get_image_files())
    
    # start the mainloop
    root.mainloop()

I believe there is a better and a nicer way of doing it than mine, but I just wanted to share it with everyone for further reference in case someone ever needs to do something alike. 

  [1]: https://i.stack.imgur.com/zu17J.png
  [2]: https://i.stack.imgur.com/N8sUV.png

</details>



huangapple
  • 本文由 发表于 2023年7月13日 22:30:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/76680546.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定