Python Tkinter的Listbox控件操作(对载入的数据列表进行增删改和导出)(选中的时候可以将所选项显示在输入框,方便修改)

1,需求:
做一个UI,实现对载入的数据列表进行增删改和导出,支持选中列表选项的时候可以将所选项显示在输入框,方便修改

2,代码实现:
这里借鉴了pyqt的界面和逻辑分离的思想,用了类的继承

1)UI类:主要实现setup_ui(frame和控件以及事件绑定)

import tkinter as tk


class ListboxUiDialog:
    def setup_ui(self, root):
        self.frame_for_listbox = tk.Frame(root, relief=tk.RAISED, padx=20, pady=10)
        self.frame_for_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.YES)

        self.frame_for_control_widget = tk.Frame(root, relief=tk.GROOVE, padx=20, pady=10)
        self.frame_for_control_widget.pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.YES)

        self.listbox_pack()
        self.control_widget_pack()

    def listbox_pack(self):
        self.listbox_name_deque = tk.Listbox(self.frame_for_listbox, selectmode="extended")
        self.listbox_name_deque.pack(side=tk.TOP, fill=tk.BOTH, expand=tk.YES)

        # 列表框绑定函数
        self.listbox_name_deque.bind('<ButtonRelease-1>', self.listbox_click)
        # self.listbox_name_deque.bind('<Up>', self.listbox_click)  # 这个不行,总是慢一拍,类似<Button-1>;要用KeyRelease-
        # self.listbox_name_deque.bind('<Down>', self.listbox_click)  # 这个不行,总是慢一拍,类似<Button-1>
        self.listbox_name_deque.bind('<KeyRelease-Up>', self.listbox_click)
        self.listbox_name_deque.bind('<KeyRelease-Down>', self.listbox_click)

        vbar = tk.Scrollbar(self.listbox_name_deque, orient=tk.VERTICAL)  # 竖直滚动条
        vbar.pack(side=tk.RIGHT, fill=tk.Y, expand=tk.NO)
        vbar.configure(command=self.listbox_name_deque.yview)

        # hbar = tk.Scrollbar(self.listbox_name_deque, orient=tk.HORIZONTAL)  # 水平滚动条【会挡住最后一行,所有直接不加了】
        # hbar.pack(side=tk.BOTTOM, fill=tk.X, expand=tk.NO)
        # hbar.configure(command=self.listbox_name_deque.xview)

        self.listbox_name_deque.config(yscrollcommand=vbar.set)  # 设置
        # self.listbox_name_deque.config(xscrollcommand=hbar.set, yscrollcommand=vbar.set)  # 设置

    def control_widget_pack(self):

        self.var_new_name = tk.StringVar()
        self.entry_new_name = tk.Entry(self.frame_for_control_widget, textvariable=self.var_new_name)
        self.entry_new_name.pack(side=tk.TOP, fill=tk.X, expand=tk.YES)

        self.btn_ini = tk.Button(self.frame_for_control_widget, text='初始化(导入文件)', command=self.ini)
        self.btn_ini.pack(side=tk.TOP, fill=tk.X, expand=tk.YES)

        self.btn_clear = tk.Button(self.frame_for_control_widget, text='清空', command=self.clear)
        self.btn_clear.pack(side=tk.TOP, fill=tk.X, expand=tk.YES)

        # self.btn2 = tk.Button(self.frame_for_control_widget, text='添加', command=self.ins)
        # self.btn2.pack(side=tk.TOP, fill=tk.X, expand=tk.YES)

        self.btn_ins = tk.Button(self.frame_for_control_widget, text='插入', command=self.ins)  # 添加和插入功能实质上是一样的
        self.btn_ins.pack(side=tk.TOP, fill=tk.X, expand=tk.YES)  # 添加和插入功能实质上是一样的

        self.btn_delt = tk.Button(self.frame_for_control_widget, text='删除', command=self.delt)
        self.btn_delt.pack(side=tk.TOP, fill=tk.X, expand=tk.YES)

        self.btn_updt = tk.Button(self.frame_for_control_widget, text='修改', command=self.updt)
        self.btn_updt.pack(side=tk.TOP, fill=tk.X, expand=tk.YES)

        self.btn_save = tk.Button(self.frame_for_control_widget, text='保存', command=self.save)
        self.btn_save.pack(side=tk.TOP, fill=tk.X, expand=tk.YES)

        self.btn_save_as = tk.Button(self.frame_for_control_widget, text='另存为', command=self.save_as)
        self.btn_save_as.pack(side=tk.TOP, fill=tk.X, expand=tk.YES)

2)主类及其逻辑实现

import tkinter as tk
from tkinter.filedialog import askopenfilename
from tkinter.filedialog import asksaveasfilename
from collections import deque
import os

from pickle_process.listbox_ui import ListboxUiDialog
from pickle_process.pickle_file import PickleFile


class ListboxMainDialog(ListboxUiDialog, tk.Toplevel):
    def __init__(self):
        super(ListboxMainDialog, self).__init__()

        self.title('列表框实验')

        self.grab_set()  # 设置为模态对话框

        screen_width = self.winfo_screenwidth()
        screen_height = self.winfo_screenheight()

        # 设置窗口大小
        win_width = 660
        win_height = 400

        x = int(screen_width / 3)
        y = int(screen_height / 4)
        self.geometry("%sx%s+%s+%s" % (win_width, win_height, x, y))
        # self.geometry('660x400')

        self.setup_ui(self)

        self.pickle_file_obj = PickleFile()
        self.pickle_filename = None

        self.mainloop()

    def listbox_click(self, event):
        """初始化 listbox"""
        cur_sels = self.listbox_name_deque.curselection()
        if cur_sels != ():
            cur = self.listbox_name_deque.selection_get()
            self.var_new_name.set(cur)

    # def ini(self):
    #     """初始化 listbox"""
    # 
    #     self.clear()
    # 
    #     self.pickle_filename = askopenfilename(filetypes=(("pickle files", "*.pickle"),),
    #                                            defaultextension='.pickle', title="加载 pickle 文件", initialdir=os.getcwd())
    #     print(f"pickle_filename:{self.pickle_filename}")
    # 
    #     if os.path.exists(self.pickle_filename):
    #         self.bt_names = self.pickle_file_obj.read_pickle_file(self.pickle_filename)  # 本来存进去的就是一个deque
    #         self.bt_names = list(self.bt_names)
    #         self.bt_names.reverse()
    #         for bt_name in self.bt_names:
    #             self.listbox_name_deque.insert(tk.END, bt_name)

    def ini(self):
        """初始化 listbox"""
        for bt_name in ["数学", "物理", "化学", "语文", "外语"]:
            self.listbox_name_deque.insert(tk.END, bt_name)

    def clear(self):
        """清空 listbox"""
        self.listbox_name_deque.delete(0, tk.END)

    def ins(self):
        """插入 listbox"""
        if self.var_new_name.get() != '':
            if self.listbox_name_deque.curselection() == ():
                self.listbox_name_deque.insert(self.listbox_name_deque.size(), self.var_new_name.get())
            else:
                self.listbox_name_deque.insert(self.listbox_name_deque.curselection(), self.var_new_name.get())

    def delt(self):
        """
            删除 listbox 元素
            目前不支持删除多个  # _tkinter.TclError: bad listbox index "1 2": must be active, anchor, end, @x,y, or a number
        :return:
        """
        if self.listbox_name_deque.curselection() != ():
            cur_sels = self.listbox_name_deque.curselection()
            print(f"cur_sels: {cur_sels}")
            for cur_sel in sorted(list(cur_sels), reverse=True):  # 要从后面往前面删除,否则删除过程中会因为次序乱掉而删了一些不是当初所选的
                self.listbox_name_deque.delete(cur_sel)

    def updt(self):
        """更新 listbox 元素"""
        if self.var_new_name.get() != '' and self.listbox_name_deque.curselection() != ():
            selected = self.listbox_name_deque.curselection()
            print(f"selected: {selected}")
            self.listbox_name_deque.delete(selected[0])  # 只删除最后一个
            self.listbox_name_deque.insert(selected, self.var_new_name.get())
        else:
            print(f"修改失败:没有选择项目或者修改内容为空")

    def remove_duplication(self, list_old):
        """去重,按原来顺序"""
        list_new = list()
        for x in list_old:
            if x not in list_new:
                list_new.append(x)
        return list_new

    def save(self):
        """保存"""
        all_content = self.listbox_name_deque.get(first=0, last=tk.END)
        print(f"all_content: {all_content}")
        all_content_list = list(all_content)
        all_content_list.reverse()
        all_content_list = self.remove_duplication(all_content_list)

        bt_name_deque = deque()
        bt_name_deque.extend(all_content_list)
        self.pickle_file_obj.write_pickle_file(bt_name_deque, pickle_filename=self.pickle_filename)

        # self.pickle_filename = None

    def save_as(self):
        """另存为"""
        all_content = self.listbox_name_deque.get(first=0, last=tk.END)
        print(f"all_content: {all_content}")
        all_content_list = list(all_content)
        all_content_list.reverse()
        all_content_list = self.remove_duplication(all_content_list)

        saveasfilename = asksaveasfilename(filetypes=(("pickle files", "*.pickle"),),
                                           initialfile=f"last_n_earphone_set_name_deque_1.pickle",
                                           defaultextension='.pickle', title="保存 pickle 文件", initialdir=os.getcwd())
        print(f"saveasfilename:{saveasfilename}")

        bt_name_deque = deque()
        bt_name_deque.extend(all_content_list)
        self.pickle_file_obj.write_pickle_file(bt_name_deque, pickle_filename=saveasfilename)


if __name__ == '__main__':

    lb_obj = ListboxMainDialog()

3)附上一个读写文件的类

from collections import deque
import pickle


class PickleFile:
    def __init__(self):
        pass

    @staticmethod
    def read_pickle_file(pickle_filename):

        try:
            with open(pickle_filename, 'rb') as fr:
                data = pickle.load(fr)
                return data
        except Exception as e:
            return None

    @staticmethod
    def write_pickle_file(data, pickle_filename):
        """保存pickle_filename"""

        try:
            with open(pickle_filename, 'wb') as fw:
                pickle.dump(data, fw)
                return True
        except Exception as e:
            return False

3,结果:
主界面

4,总结要点:
1)# 列表框绑定函数
self.listbox_name_deque.bind(‘’, self.listbox_click)
self.listbox_name_deque.bind(‘’, self.listbox_click)
self.listbox_name_deque.bind(‘’, self.listbox_click)
2)tk.Toplevel解决输入框的set()不起作用的情况
3)使用self.grab_set()将主界面设置为模态对话框,这样不至于导入一个文件操作的时候,本界面“不见了”

5,参考
(八)Python 图形化界面设计

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值