效果
效果如下:
用鼠标拖动,交换第一张图片和第二张图片的位置。
代码
import fnmatch
import os
from tkinter import *
import numpy as np
from PIL import ImageTk
from PIL import Image as imim # 要用别名
def changeOrder(widget1, widget2, initial):
"""
实现组件位置互换的操作
:param widget1:首次鼠标点击拖拽的标签对象
:param widget2:拖拽中根据鼠标位置 获得的标签对象
:param initial:首次鼠标点击拖拽的初始位置 grid布局
"""
target = widget1.grid_info()
# 拖拽过程中,鼠标的位置未进入另一个组件里面 会报错
try:
widget1.grid(row=initial['row'], column=initial['column'])
widget2.grid(row=target['row'], column=target['column'])
except KeyError:
pass
def on_click(event):
"""
用于判断是否点击,释放鼠标按钮
:param event: 点击事件
"""
widget = event.widget
# 判断发生事件的是否是组件
if isinstance(widget, Label):
# 鼠标点击事件的初始位置,不是组件的位置
start = (event.x, event.y)
# 发生事件的组件的位置
grid_info = widget.grid_info()
# 移动图片
widget.bind("<B1-Motion>", lambda event: drag_motion(event, widget, start))
# 放置图片
widget.bind("<ButtonRelease-1>", lambda event: drag_release(event, widget, grid_info))
else:
root.unbind("<ButtonRelease-1>")
def drag_motion(event, widget, start):
"""
用于拖拽label标签
:param event: 鼠标点击事件
:param widget: 发生事件的label
:param start: 发生事件的label的初始位置
"""
# 获取组件当前位置(随鼠标移动而改变,x,y基于组件左上角边角位置决定)
x = widget.winfo_x() + event.x - start[0]
y = widget.winfo_y() + event.y - start[1]
# label组件置顶
widget.lift()
# 根据place实现拖拽移动label
widget.place(x=x, y=y)
def drag_release(event, widget, grid_info):
"""
:param event: 鼠标释放事件
:param widget: 发生事件的label标签
:param grid_info: label标签原来的位置
"""
# 组件释放后置于底层
widget.lower()
# UI界面相对于电脑屏幕的x,y位置
x, y = root.winfo_pointerxy()
# 根据相对位置,确定target是哪个组件对象
target_widget = root.winfo_containing(x, y)
# 如果target是组件对象,则换位置
if isinstance(target_widget, Label):
changeOrder(target_widget, widget, grid_info)
# 否则点击事件的组件回归到原来位置
else:
widget.grid(row=grid_info['row'], column=grid_info['column'])
root = Tk()
root.geometry("1920x1080")
# 图片文件路径
filepath = "/Users/resource"
# -------------遍历文件获取图片的文件路径
# 符合images的文件路径列表
matches = []
images = ['*.jpg', '*.png','*.jpeg']
n = []
# 平方列表
power = []
x = 0
y = 0
x_list = []
y_list = []
# 计算平方值 根据图片数量分割
for i in range(1, 1000):
power.append(i * i)
for dirpath, dirnames, filenames in os.walk(os.path.expanduser(filepath)): # 遍历返回三元组
for image in images:
for filename in fnmatch.filter(filenames, image):
matches.append(os.path.join(dirpath, filename)) # 放入符合文件后缀的路径
# 获取 i,也就是分多少份
for i in range(len(power)):
if power[i] >= len(matches):
power.append(i + 1)
break
# 自动适应大小,根据UI界面分割i份
height = np.around((root.winfo_vrootheight()-96) / power[-1], decimals=0)
width = np.around((root.winfo_vrootwidth()-54) / power[-1], decimals=0)
height = int(height)
width = int(width)
# 自动排列图片
for i in range(1, power[-1] + 1):
for j in range(1, power[-1] + 1):
if i == 1:
x_list.append(x)
width = width
x = width + x
y_list.append(y)
height = height
y = height + y
# 根据图片数量,定义多个对象
for i in range(len(matches)):
n.append("myLabel" + f"{i}")
# 列数 比如9张图分3份 也就是0 1 2
c = 0
# 行数 比如9张图分3份 也就是0 1 2
r = 0
for i in range(len(matches)):
# 求列数
column = int((c+3)/3)-1
# 求行数
row = int((r+3)/3)-1
# 打开图片
image = imim.open(matches[i])
# 自动适应修改图片大小
image = image.resize((width, height))
# 把label标签设置成图片(label参数)
img_png = ImageTk.PhotoImage(image)
# 创建label组件
n[i] = Label(root, image=img_png, text=f"Label {i}", borderwidth=2, relief="raise")
# 之前CSDN已经发过相关问题的解决, label标签设置成图片变白,无法显示图片
# 这句必须要加上,否则图片无法显示
n[i].image = img_png
# 使用grid布局,做这个东西使用了另一种方法bug太多,所以grid布局好做一点
n[i].grid(row=row, column=column, padx=2, pady=2, sticky=E + W + S + N)
# 求行数
if (i+1) % 3 == 0:
r = r + 3
if r == len(matches):
r = 0
# 求列数
else:
c = c + 3
if c == len(matches):
c = 0
# 点击事件绑定
root.bind("<Button-1>", on_click)
root.mainloop()