吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1773|回复: 25
收起左侧

[Python 原创] Tkinter 制作的NotePad记事本

  [复制链接]
你听说了吗? 发表于 2023-9-5 00:05
本帖最后由 你听说了吗? 于 2023-9-5 17:08 编辑

最近在学习tkinter, 界面丑是丑了点,但是打包方便,比pyqt什么的小多了,功能比界面重要:
下面是源码,供大家参考:
源代码:

import tkinter as tk
from tkinter import *
from tkinter import filedialog, messagebox, END
import os
import sys

class NotePad(tk.Tk):
    icons = ["new_file", "open_file", "save", "cut", "copy",
             "paste", "undo", "redo", "find_text"]
    icon_res = []

    theme_color = {"Default": "#000000.#FFFFFF",
                   "Olive Green": "#D1E7E0.#5B8340",
                   "Night Mode": "#FFFFFF.#000000"}
    # 初始化操作

    def __init__(self):
        super().__init__()
        self.file_name = None
        self.set_window()
        self.create_menu_bar()
        self.create_tool_bar()
        self.create_body()
        self.create_pop_menu()

    # 设置窗口界面
    def set_window(self):
        self.title("NotePad")
        max_width, max_height = self.maxsize()
        align_center = "800x600+%d+%d" % ((max_width-800)/2,
                                          (max_height-600)/2)
        self.geometry(align_center)  # 设置居中
        self.iconbitmap("D:\\notepad\\img\\editor.ico")

    # 创建菜单项目
    def create_menu_bar(self):
        menu_bar = Menu(self)
        # 添加菜单项目
        file_menu = Menu(menu_bar, tearoff=False)
        file_menu.add_command(
            label="新建", accelerator="Ctrl+N", command=self.new_file)
        file_menu.add_command(
            label="打开", accelerator="Ctrl+O", command=self.open_file)
        file_menu.add_command(
            label="保存", accelerator="Ctrl+S", command=self.save_file)
        file_menu.add_command(
            label="另存为", accelerator="Alt+S", command=self.save_as)
        file_menu.add_separator()
        file_menu.add_command(
            label="关闭", command=self.new_file)
        file_menu.add_command(
            label="退出", accelerator="Alt+E", command=self.exit_notepad)
        menu_bar.add_cascade(label="文件", menu=file_menu)

        edit_menu = Menu(menu_bar, tearoff=False)
        edit_menu.add_command(label="撤销", accelerator="Ctrl+Z",
                              command=lambda: self.handle_menu_action("撤销"))
        edit_menu.add_command(label="恢复", accelerator="Ctrl+Y",
                              command=lambda: self.handle_menu_action("恢复"))
        edit_menu.add_separator()
        edit_menu.add_command(label="剪切", accelerator="Ctrl+X",
                              command=lambda: self.handle_menu_action("剪切"))
        edit_menu.add_command(label="复制", accelerator="Ctrl+C",
                              command=lambda: self.handle_menu_action("复制"))
        edit_menu.add_command(label="粘贴", accelerator="Ctrl+V",
                              command=lambda: self.handle_menu_action("粘贴"))
        edit_menu.add_separator()
        edit_menu.add_command(label="查找", accelerator="Ctrl+F",
                              command=self.find_text_dialog)
        edit_menu.add_command(label="全选", accelerator="Ctrl+A",
                              command=self.select_all)
        menu_bar.add_cascade(label="编辑", menu=edit_menu)

        view_menu = Menu(menu_bar, tearoff=False)
        # 显示行号
        self.is_show_line_num = IntVar()
        self.is_show_line_num.set(1)
        view_menu.add_checkbutton(label="显示行号",
                                  onvalue=0, offvalue=1,
                                  variable=self.is_show_line_num,
                                  command=self.update_line_num)
        # 高亮当前行
        self.is_heighlight_line = IntVar()
        view_menu.add_checkbutton(label="高亮当前行",
                                  variable=self.is_heighlight_line, command=self.toggle_highlight)
        # 主题
        theme_menu = Menu(menu_bar, tearoff=False)
        self.theme_choice = StringVar()
        self.theme_choice.set("Default")
        for k in sorted(self.theme_color):
            theme_menu.add_radiobutton(
                label=k, variable=self.theme_choice, command=self.change_theme)

        view_menu.add_cascade(label="主题", menu=theme_menu)
        menu_bar.add_cascade(label="视图", menu=view_menu)

        about_menu = Menu(menu_bar, tearoff=False)
        about_menu.add_cascade(
            label="关于", command=lambda: self.show_message("关于"))
        about_menu.add_cascade(
            label="帮助", command=lambda: self.show_message("帮助"))
        menu_bar.add_cascade(label="关于", menu=about_menu)

        self['menu'] = menu_bar

    # 工具栏
    def create_tool_bar(self):
        tool_bar = Frame(self,  height=25, bg="#FFFFFF")
        # 填充x轴
        tool_bar.pack(fill="x")
        for icon in self.icons:
            tool_icon = PhotoImage(file="D:\\notepad\\img\\%s.gif" % (icon, ))
            tool_btn = Button(tool_bar, image=tool_icon,
                              command=self.tool_bar_action(icon))
            tool_btn.pack(side="left")
            # 将tool_icon添加到icon_res
            self.icon_res.append(tool_icon)

    # 界面操作主体
    def create_body(self):
        # 左边行号,中间是文本编辑区, 右边是滚动条
        # 行号区域
        self.line_number_bar = Text(self, width=4, takefocus=0, border=0,
                                    background="#F0E68C", state="disabled")
        self.line_number_bar.pack(side="left", fill="y")
        # 文本编辑区
        # wrap如何换行, word表示自动换行
        # undo =True开启撤销功能
        self.context_text = Text(self, wrap="word", undo=True)
        # 热键绑定
        self.context_text.bind("<Control-o>", self.open_file)
        self.context_text.bind("<Control-O>", self.open_file)
        self.context_text.bind("<Control-s>", self.save_file)
        self.context_text.bind("<Control-S>", self.save_file)
        self.context_text.bind("<Control-n>", self.new_file)
        self.context_text.bind("<Control-N>", self.new_file)
        self.context_text.bind("<Alt-s>", self.save_as)
        self.context_text.bind("<Alt-S>", self.save_as)
        self.context_text.bind("<Alt-e>", self.exit_notepad)
        self.context_text.bind("<Alt-E>", self.exit_notepad)
        self.context_text.bind("<Control-F>", self.find_text_dialog)
        self.context_text.bind("<Control-f>", self.find_text_dialog)
        self.context_text.bind(
            "<Any-KeyPress>", lambda e: self.update_line_num())
        self.context_text.pack(fill="both", expand=1)

        # 设置文本输入区
        self.context_text.tag_config("active_line", background="#EEEEE0")
        # 滚动条
        scroll_bar = Scrollbar(self.context_text)
        scroll_bar['command'] = self.context_text.yview
        self.context_text["yscrollcommand"] = scroll_bar.set
        scroll_bar.pack(side="right", fill="y")

    # 打开文件
    def open_file(self, event=None):
        # 对打开文件的类型进行设置
        input_file = filedialog.askopenfilename(
            filetypes=[("所有文件", "*.*"), ("文本文档", "*.txt")])
        if input_file:
            self.title("{}***NotePad".format(os.path.basename(input_file)))
            self.file_name = input_file
            self.context_text.delete(1.0, END)
            with open(input_file, 'r') as _file:
                self.context_text.insert(1.0, _file.read())

    # 保存文件
    def write_to_file(self, file_name):
        try:
            content = self.context_text.get(1.0, END)
            with open(file_name, 'w') as _file:
                _file.write(content)
            self.title("{}---NotePad".format(os.path.basename(file_name)))
        except IOError:
            messagebox.showinfo("提示", "文件保存失败!")

    def save_file(self, event=None):
        if not self.file_name:
            self.save_as()
        else:
            self.write_to_file(self.file_name)

    # 新建文件
    def new_file(self, event=None):
        self.title("新建---NotePad")
        self.context_text.delete(1.0, END)
        self.file_name = None

    # 另存为
    def save_as(self, event=None):
        input_file = filedialog.asksaveasfilename(
            filetypes=[("所有文件", "*.*"), ("文本文档", "*.txt")],
            defaultextension="txt")
        if input_file:
            self.file_name = input_file
            self.write_to_file(self.file_name)

    # 退出
    def exit_notepad(self, event=None):
        if messagebox.askokcancel("退出", "确定退出吗?"):
            self.destroy()

    # 右键弹出菜单

    def create_pop_menu(self):
        pop_nemu = Menu(self.context_text, tearoff=False)
        for item1, item2 in zip(['复制', '粘贴', '剪切', '撤销', '恢复'],
                                ['copy', 'paste', 'cut', 'undo', 'redo']):
            pop_nemu.add_command(label=item1, compound='left',
                                 command=self.tool_bar_action(item2))
        pop_nemu.add_separator()
        pop_nemu.add_command(label="全选", command=self.select_all)
        # 绑定
        self.context_text.bind("<Button-3>",
                               lambda event: pop_nemu.tk_popup(event.x_root, event.y_root))

    # 右键菜单的处理
    def handle_menu_action(self, action_type):
        if action_type == "复制":
            self.context_text.event_generate("<<Copy>>")
        elif action_type == "粘贴":
            self.context_text.event_generate("<<Paste>>")
        elif action_type == "剪切":
            self.context_text.event_generate("<<Cut>>")
        elif action_type == "撤销":
            self.context_text.event_generate("<<Undo>>")
        elif action_type == "恢复":
            self.context_text.event_generate("<<Redo>>")

        # 防止事件传递
        return "break"

    # 工具栏命令处理
    def tool_bar_action(self, action_type):
        def handle():
            if action_type == 'open_file':
                self.open_file()
            elif action_type == 'save':
                self.save_file()
            elif action_type == "new_file":
                self.new_file()
            elif action_type == "cut":
                self.handle_menu_action("剪切")
            elif action_type == "copy":
                self.handle_menu_action("复制")
            elif action_type == "paste":
                self.handle_menu_action("粘贴")
            elif action_type == "undo":
                self.handle_menu_action("撤销")
            elif action_type == "redo":
                self.handle_menu_action("恢复")
            elif action_type == "find_text":
                self.find_text_dialog()
        # handle返回处理
        return handle

    # 全选
    def select_all(self):
        self.context_text.tag_add('sel', 1.0, END)
        return "break"

    # 行号处理
    def update_line_num(self):
        if self.is_show_line_num.get() == 0:
            # 获取所有行
            row, col = self.context_text.index(END).split('.')
            # 列出每行的行号
            line_num_content = "\n".join([str(i) for i in range(1, int(row))])
            self.line_number_bar.config(state="normal")
            self.line_number_bar.delete(1.0, END)
            self.line_number_bar.insert(1.0, line_num_content)
            self.line_number_bar.config(state='disabled')
        else:
            self.line_number_bar.config(state='normal')
            self.line_number_bar.delete(1.0, END)
            self.line_number_bar.config(state='disabled')

    # 高亮当前行
    def toggle_highlight(self):
        if self.is_heighlight_line.get():
            self.context_text.tag_remove('active_line', 1.0, END)
            # 设置高亮
            self.context_text.tag_add(
                'active_line', "insert linestart", "insert lineend+1c")
            # 通过轮询递归的方式进行处理
            self.context_text.after(200, self.toggle_highlight)
        else:
            self.context_text.tag_remove("active_line", 1.0, END)

    # 文本查找功能
    def find_text_dialog(self, event=None):
        search_dialog = Toplevel(self)
        search_dialog.title('查找文本')
        max_width, max_height = self.maxsize()
        align_center = "300x80+%d+%d" % ((max_width-300)/2,
                                         (max_height-80)/2)
        search_dialog.geometry(align_center)  # 设置居中
        search_dialog.resizable(False, False)
        Label(search_dialog, text='查找全部').grid(row=0, column=0, sticky='e')
        search_text = Entry(search_dialog, width=25)
        search_text.grid(row=0, column=1, padx=2, pady=2, sticky="we")
        search_text.focus_set()
        # 忽略大小写
        ignore_case_value = IntVar()
        Checkbutton(search_dialog, text="忽略大小写", variable=ignore_case_value).grid(
            row=1, column=1, sticky='e', padx=2, pady=2)
        Button(search_dialog, text='查找',
               command=lambda: self.search_result(search_text.get(), ignore_case_value.get(), search_dialog, search_text)).grid(
            row=0, column=2, sticky="w"+"e", padx=2, pady=2)

        def close_serach_dialog():
            self.context_text.tag_remove('match', 1.0, END)
            search_dialog.destroy()

        search_dialog.protocol("WM_DELETE_WINDOW", close_serach_dialog)
        return "break"

    # 查找方法
    def search_result(self, key, ignore_case, search_dialog, search_box):
        self.context_text.tag_remove('match', 1.0, END)
        match_found = 0
        if key:
            start_pos = 1.0
            while True:
                start_pos = self.context_text.search(
                    key, index=start_pos, nocase=ignore_case, stopindex=END)
                if not start_pos:
                    break
                end_pos = "{}+{}c".format(start_pos, len(key))
                self.context_text.tag_add('match', start_pos, end_pos)
                match_found += 1
                start_pos = end_pos
            self.context_text.tag_config(
                'match', foreground='red', background='yellow')
        search_box.focus_set()
        search_dialog.title("发现了%d个匹配" % match_found)

    # 主题切换
    def change_theme(self):
        selected_theme = self.theme_choice.get()
        fg_bg = self.theme_color.get(selected_theme)
        fg_color, bg_color = fg_bg.split(".")
        print(fg_color, bg_color)
        self.context_text.config(bg=bg_color, fg=fg_color)

    # 关于菜单
    def show_message(self, type):
        if type == "帮助":
            messagebox.showinfo("帮助", "这是帮助文档")
        else:
            messagebox.showinfo("关于", "这是一个简单的记事本程序")

if __name__ == "__main__":
    app = NotePad()
    app.mainloop()

打包方法
使用auto-py-to-exe打包;
单独复制img目录何notepad.py文件到另一目录;
修改源代码中的img文件路径为img目录的绝对路径;
打包成单个文件;
选着资源文件夹;
打包生成exe可执行文件;



image.png




缺少的img文件补上;

img.zip

6.82 KB, 下载次数: 21, 下载积分: 吾爱币 -1 CB

免费评分

参与人数 6吾爱币 +14 热心值 +5 收起 理由
crary06 + 1 我很赞同!
lccccccc + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
苏紫方璇 + 10 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
EHOOD + 1 谢谢@Thanks!
lookfeiji + 1 + 1 就凭这代码量,就值得点一波
xyl52p + 1 + 1 谢谢@Thanks!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

lookfeiji 发表于 2023-9-5 14:00
本帖最后由 lookfeiji 于 2023-9-5 14:08 编辑

自己搞几个.gif的文件"new_file", "open_file", "save", "cut", "copy", "paste", "undo", "redo", "find_text"命这几个名字,还有一个editor.ico图标文件,丑是丑了点,能用
1693894184723.jpg
dreamrise 发表于 2023-9-5 10:16
52bojie 发表于 2023-9-5 08:56
头像被屏蔽
yyb1813 发表于 2023-9-5 09:04
提示: 作者被禁止或删除 内容自动屏蔽
apull 发表于 2023-9-5 09:09
这个不错,学习了。
scbzwv 发表于 2023-9-5 09:11
感谢分享
covenlonki 发表于 2023-9-5 09:18
感谢分享,值得学习学习
zhanglei1371 发表于 2023-9-5 09:26
看起来比c#实现复杂太多了
boxer 发表于 2023-9-5 09:40
实现这些功能,py的代码不少了,跟aardio比的话
gufengaoyue 发表于 2023-9-5 11:16


行号是固定的,并不能随滚动条变化。
不知道是故意这样设计,还是没注意到。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-5-22 07:13

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表