from tkinter import *
from tkinter import ttk, Button, Canvas, Listbox, Entry, LabelFrame, IntVar, Checkbutton, messagebox
import win32print
root = Tk()
root.title("tkinter Listbox 列表框实现多列对齐排列")
root.geometry('550x450')
def callback2(t, event=None):
# 复制操作
t.event_generate('<<Copy>>')
def popup(top, t, event=None):
menu = Menu(top, tearoff=False)
menu.add_command(label="复制", command=lambda *e: callback2(t))
menu.post(event.x_root, event.y_root) # post在指定的位置显示弹出菜单
def delete_item(listbox, all_data_dic0, printers_valus2):
# 获取Listbox中所有被选中的项目
selected_items = listbox.curselection()
# 对选中的项目进行逆序排列,以免删除项目影响后面的索引
# selected_items.reverse()
item = selected_items[0]
temp_value = listbox.get(item)
# listbox.delete(item)
if messagebox.askokcancel("删除", "确定删除吗?"):
temp_value2 = all_data_dic0[item]
delete_sql = f'delete from data_table where byname="%s"'
# link.insert_one(delete_sql % temp_value2['Name0'])
# register_form(printers_valus2=printers_valus2) # 刷新页面
def on_entry_click(event, entry, default_value):
# 鼠标点击输入框
if entry.get() == default_value:
# entry.delete(0, END)
pass
else:
print(f'entry.get():{entry.get()} default_value:{default_value}')
def on_mouse_leave(event, entry, default_value):
# 鼠标离开事件
if not entry.get().strip():
entry.delete(0, END)
entry.insert(0, default_value)
def on_entry_clicked1(entry0, entry2, pos_name, printers_valus2, item_value1, new_child_window, listbox, item):
# 保存修改数据
byname = entry0.get().strip()
sizename = entry2.get().strip()
printername = pos_name.get()
byname_old = item_value1['Name0']
is_update = False
search_sql = 'select id from data_table where byname="%s" and sizename="%s" and printername="%s"'
# exist = link.one_search(search_sql % (byname, sizename, printername), "data_table")
# print('exist:', exist)
# if exist:
# print('此名称0已经存在')
# notification.notify(title='此名称0已经存在', message='此名称0已经存在', timeout=5)
# is_update = False
# else:
# is_update = True
#
# if is_update:
# update_sql = f'update data_table set byname="%s",sizename="%s",printername="%s" where byname="%s"'
# # print(update_sql % (byname, sizename, printername, byname_old))
# link.insert_one(update_sql % (byname, sizename, printername, byname_old))
# register_form(printers_valus2=printers_valus2) # 刷新页面
new_child_window.destroy()
def on_entry_clicked2(entry0, entry2, pos_name, printers_valus2, new_child_window):
# 保存新增数据
byname = entry0.get().strip()
sizename = entry2.get().strip()
printername = pos_name.get()
search_sql = 'select id from data_table where byname="%s" and sizename="%s" and printername="%s"'
# exist = link.one_search(search_sql % (byname, sizename, printername), "data_table")
# if exist:
# print('此名称0已经存在')
# notification.notify(title='此名称0已经存在', message='此名称0已经存在', timeout=5)
# else:
# sql = 'insert into data_table(byname, sizename, printername) values(%r,%r,%r)'
# link.insert_one(sql % (byname, sizename, printername))
# register_form(printers_valus2=printers_valus2)
new_child_window.destroy()
def create_frame(printers_valus2, event):
# 创建一个新的Frame
new_child_window = Toplevel()
new_child_window.title('新增')
# new_child_window.iconphoto(False, PhotoImage(data=iconphoto_path)) # 图标
new_child_window.geometry(f'280x180+{event.x_root}+{event.y_root}') # menu.post(event.x_root, event.y_root)
label1 = Label(new_child_window, text='名称0:')
label1.place(relx=0, rely=0.02, relheight=0.2, relwidth=0.55)
entry0 = Entry(new_child_window, textvariable='name0')
entry0.place(relx=0.46, rely=0.02, relheight=0.2, relwidth=0.45)
# 插入默认值
entry0.delete(0, END)
entry0.insert(0, '名称1')
# 绑定点击事件
entry0.bind("<Button-1>", lambda event: on_entry_click(event, entry0, '名称1'))
entry0.bind("<Leave>", lambda event: on_mouse_leave(event, entry0, '名称1'))
label2 = Label(new_child_window, text='名称1:')
label2.place(relx=0, rely=0.24, relheight=0.2, relwidth=0.55)
entry2 = Entry(new_child_window, textvariable='name2')
entry2.place(relx=0.46, rely=0.24, relheight=0.2, relwidth=0.45)
# 插入默认值
entry2.delete(0, END)
entry2.insert(0, "70*50")
# 绑定点击事件
entry2.bind("<Button-1>", lambda event: on_entry_click(event, entry2, "70*50"))
entry2.bind("<Leave>", lambda event: on_mouse_leave(event, entry2, "70*50"))
# 选择名称2
label3 = Label(new_child_window, text='选择名称2:', bd=3, width=16)
label3.place(relx=0, rely=0.46, relwidth=0.55, relheight=0.2)
# 获取默认打印机名称
show_device_name = win32print.GetDefaultPrinter()
if show_device_name not in printers_valus2:
show_device_name = printers_valus2[0]
# 下拉框
pos_name = ttk.Combobox(new_child_window, width=20, height=10, textvariable=show_device_name, state='readonly')
# 设置Combobox的下拉选项
pos_name['values'] = printers_valus2
pos_name.place(relx=0.46, rely=0.46, relwidth=0.45, relheight=0.2)
if show_device_name in printers_valus2:
pos_name.current(printers_valus2.index(show_device_name))
else:
pos_name.current(0)
# 保存新增的数据
confirm_btn = Button(new_child_window, text='确认', width=10,
command=lambda: on_entry_clicked2(entry0, entry2, pos_name, printers_valus2,
new_child_window))
confirm_btn.place(relx=0.3, rely=0.72, relheight=0.2, relwidth=0.35)
def edit_frame(event, item, all_data_dic0, printers_valus2, listbox):
# 创建一个新的Frame
item_value = listbox.get(item)
# print('item_value:', item_value)
# print('item_value1:', all_data_dic0[item])
item_value1 = all_data_dic0[item]
new_child_window = Toplevel()
new_child_window.title('编辑')
# new_child_window.iconphoto(False, PhotoImage(data=iconphoto_path)) # 设置图标
new_child_window.geometry(f'280x180+{event.x_root}+{event.y_root}') # menu.post(event.x_root, event.y_root)
label1 = Label(new_child_window, text='名称0:')
label1.place(relx=0, rely=0.02, relheight=0.2, relwidth=0.55)
entry0 = Entry(new_child_window, textvariable='name0')
entry0.place(relx=0.46, rely=0.02, relheight=0.2, relwidth=0.45)
# 插入默认值
entry0.delete(0, END)
entry0.insert(0, item_value1['Name0'])
# 绑定点击事件
entry0.bind("<Button-1>", lambda event: on_entry_click(event, entry0, item_value1['Name0']))
entry0.bind("<Leave>", lambda event: on_mouse_leave(event, entry0, item_value1['Name0']))
label2 = Label(new_child_window, text='名称1:')
label2.place(relx=0, rely=0.24, relheight=0.2, relwidth=0.55)
entry2 = Entry(new_child_window, textvariable='name2')
entry2.place(relx=0.46, rely=0.24, relheight=0.2, relwidth=0.45)
# 插入默认值
entry2.delete(0, END)
entry2.insert(0, item_value1['Name'])
# 绑定点击事件
entry2.bind("<Button-1>", lambda event: on_entry_click(event, entry2, item_value1['Name']))
entry2.bind("<Leave>", lambda event: on_mouse_leave(event, entry2, item_value1['Name']))
# 选择名称2
label3 = Label(new_child_window, text='选择名称2:', bd=3, width=16)
label3.place(relx=0, rely=0.46, relwidth=0.55, relheight=0.2)
# 获取默认打印机设备
show_device_name = item_value1['Name2']
# 下拉框
pos_name = ttk.Combobox(new_child_window, width=20, height=10, textvariable=show_device_name, state='readonly')
printers_temp = ['OneNote for Windows 10', '导出为WPS PDF', 'Microsoft XPS Document Writer',
'Microsoft Print to PDF', 'Fax']
# 设置Combobox的下拉选项
pos_name['values'] = printers_valus2
pos_name.place(relx=0.46, rely=0.46, relwidth=0.45, relheight=0.2)
if show_device_name in printers_valus2:
pos_name.current(printers_valus2.index(show_device_name))
else:
pos_name.current(0)
# 保存修改的数据
confirm_btn = Button(new_child_window, text='确认', width=10,
command=lambda: on_entry_clicked1(entry0, entry2, pos_name, printers_valus2, item_value1,
new_child_window, listbox, item))
confirm_btn.place(relx=0.3, rely=0.72, relheight=0.2, relwidth=0.35)
def get_display_width(s):
width = 0
for char in s:
width += len(char.encode(sys.stdout.encoding))
return width
def show_menu(listbox, all_data_dic0, printers_valus2, event, top=None):
# 根据当前位置获取Listbox中选中的项目
try:
item = listbox.curselection()[0]
except IndexError:
item = 0
# 创建菜单,并添加选项
menu = Menu(top, tearoff=0)
if item > 0:
menu.add_command(label="删除", command=lambda: delete_item(listbox, all_data_dic0, printers_valus2))
menu.add_separator() # 分割线
menu.add_command(label="编辑",
command=lambda: edit_frame(event, item, all_data_dic0, printers_valus2, listbox))
menu.add_separator() # 分割线
menu.add_command(label="复制", command=lambda: callback2(listbox))
menu.add_separator() # 分割线
# 如果选中了项目,就在菜单中显示项目的值
if item or item == 0:
menu.add_command(label="增加", command=lambda: create_frame(printers_valus2, event))
# 在相应位置显示菜单
menu.post(event.x_root, event.y_root)
def show_listbox(printers_valus2, select_value=None):
# 展示列表框内容
scrollbar = Scrollbar(root)
scrollbar.place(relx=0.85, rely=0.52, relheight=0.46, relwidth=0.035)
listbox = Listbox(root, width=90, font='TkDefaultFont 11')
listbox.place(relx=0.1, rely=0.52, relheight=0.46, relwidth=0.75)
# 1. 从 sqlite3 中 读取数据
# if not select_value or select_value == '全部':
# all_data = link.all_search('select byname,sizename,printername from data_table', 'data_table')
# else:
# all_data = link.all_search(
# 'select byname,sizename,printername from data_table where printername="%s"' % select_value, 'data_table')
# allStudents = [{'Name0': i[0], 'Name': i[1], 'Name2': i[2]} for i in all_data]
# 2. 测试数据
allStudents_all = [{'Name0': 'USER', 'Name': '1041*1524', 'Name2': 'TSC TTP-244 Pro'},
{'Name0': '100*100', 'Name': '1025*1000', 'Name2': 'TSC TTP-244 Pro'},
{'Name0': '100*150', 'Name': '1025*1500', 'Name2': 'TSC TTP-244 Pro'},
{'Name0': '100x100', 'Name': '250*150', 'Name2': 'TSC TTP-244 Pro'},
{'Name0': '111', 'Name': '876*700', 'Name2': 'TSC TTP-244 Pro'},
{'Name0': '2 x 4', 'Name': '533*1016', 'Name2': 'TSC TTP-244 Pro'},
{'Name0': '4 x 4', 'Name': '1041*1016', 'Name2': 'TSC TTP-244 Pro'},
{'Name0': '4 x 6', 'Name': '1041*1524', 'Name2': 'TSC TTP-244 Pro'},
{'Name0': '45*30', 'Name': '475*300', 'Name2': 'TSC TTP-244 Pro'},
{'Name0': '50*30', 'Name': '525*300', 'Name2': 'TSC TTP-244 Pro'},
{'Name0': '70*30', 'Name': '725*300', 'Name2': 'TSC TTP-244 Pro'},
{'Name0': '70*50', 'Name': '725*500', 'Name2': 'TSC TTP-244 Pro'},
{'Name0': '新卷', 'Name': '525*300', 'Name2': 'TSC TTP-244 Pro'},
{'Name0': '名称1', 'Name': '676*300', 'Name2': 'TSC TTP-244 Pro'},
{'Name0': '名称2', 'Name': '1026*1500', 'Name2': 'TSC TTP-244 Pro'},
{'Name0': 'USER', 'Name': '1041*1016', 'Name2': 'Gprinter GP-1324D'},
{'Name0': '100*100', 'Name': '1025*1000', 'Name2': 'Gprinter GP-1324D'},
{'Name0': '100*150', 'Name': '1025*1500', 'Name2': 'Gprinter GP-1324D'},
{'Name0': '100x100', 'Name': '250*150', 'Name2': 'Gprinter GP-1324D'},
{'Name0': '111', 'Name': '876*700', 'Name2': 'Gprinter GP-1324D'},
{'Name0': '2 x 4', 'Name': '533*1016', 'Name2': 'Gprinter GP-1324D'},
{'Name0': '4 x 4', 'Name': '1041*1016', 'Name2': 'Gprinter GP-1324D'},
{'Name0': '4 x 6', 'Name': '1041*1524', 'Name2': 'Gprinter GP-1324D'},
{'Name0': '45*30', 'Name': '475*300', 'Name2': 'Gprinter GP-1324D'},
{'Name0': '50*30', 'Name': '525*300', 'Name2': 'Gprinter GP-1324D'},
{'Name0': '70*30', 'Name': '725*300', 'Name2': 'Gprinter GP-1324D'},
{'Name0': '70*50', 'Name': '725*500', 'Name2': 'Gprinter GP-1324D'},
{'Name0': '新卷', 'Name': '525*300', 'Name2': 'Gprinter GP-1324D'},
{'Name0': '名称1', 'Name': '676*300', 'Name2': 'Gprinter GP-1324D'},
{'Name0': '名称2', 'Name': '1026*1500', 'Name2': 'Gprinter GP-1324D'}]
if not select_value or select_value == '全部':
allStudents = allStudents_all
else:
allStudents = []
for i in allStudents_all:
if select_value in i['Name2']:
allStudents.append(i)
maxspace0 = get_display_width(max(allStudents, key=lambda x: get_display_width(x['Name0']))['Name0']) + 4
maxspace = get_display_width(max(allStudents, key=lambda x: get_display_width(x['Name']))['Name']) + 4
# 一个汉字占两个字符
listbox.insert(END, f"{'名称0'.ljust(maxspace0 - 2)} {'名称1'.ljust(maxspace - 2)} 名称2")
all_data_dic0 = {}
for i in range(len(allStudents)):
tem = 0
for l in allStudents[i]['Name0']:
if get_display_width(l) > 1:
tem += 1
tem1 = 0
for l1 in allStudents[i]['Name']:
if get_display_width(l1) > 1:
tem1 += 1
text1 = f"{allStudents[i]['Name0'].ljust(maxspace0 - tem)} {allStudents[i]['Name'].ljust(maxspace - tem1)} {allStudents[i]['Name2']}"
listbox.insert(END, text1)
all_data_dic0[i + 1] = allStudents[i]
listbox.configure(yscrollcommand=scrollbar.set)
scrollbar.configure(command=listbox.yview)
# 左键点击列表框中的值, 并执行后续操作
# listbox.bind('<<ListboxSelect>>', lambda event: listbox_select(listbox.curselection(), listbox, event, all_data_dic, combobox, printers_valus2))
# 绑定右键菜单
listbox.bind("<Button-3>",
lambda event: show_menu(listbox, all_data_dic0, printers_valus2, event))
def update_listbox(event, printers_valus2, select_value):
# 更新列表框的内容
print('select_value:', select_value)
show_listbox(printers_valus2, select_value)
def register_form(printers_valus2=None):
# 销毁所有滚动条和列表框
# 与下拉框内容绑定
combobox6 = ttk.Combobox(root, width=20, height=10)
printers_temp = ['OneNote for Windows 10', '导出为WPS PDF', 'Microsoft XPS Document Writer', 'Microsoft Print to PDF',
'Fax']
printers_valus3 = list(set(printers_valus2).difference(set(printers_temp)))
printers_valus3.insert(0, '全部')
print('printers_valus3:', printers_valus3)
# 设置Combobox的下拉选项
combobox6['values'] = printers_valus3
combobox6.bind("<<ComboboxSelected>>", lambda event: update_listbox(event, printers_valus2, combobox6.get()))
combobox6.place(relx=0.1, rely=0.43, relheight=0.08, relwidth=0.75)
try:
combobox6.current(0)
except:
pass
# 展示列表框内容
show_listbox(printers_valus2)
register_form(['Gprinter GP-1324D', 'TSC TTP-244 Pro'])
mainloop()