英文:
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's been quite a while, but I'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's unnecessary. Though in the beginning I thought the opposite. Anyway, these days I'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.
> Before I post the pictures and the code I want to say thank you to
> 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'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 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("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 backgorund 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_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>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论