【摸鱼小工具】txt文本裁剪工具,tkinter练习


前言

上班了以后经常会有很多奇思妙想,就想用我仅有的一点代码能力在空闲时间实现它们,也不知道能坚持多久,就从这里开始吧!
本文记录了实现一个拥有图形化界面的txt裁剪小程序,可以实现在不需要手动打开这个txt文件的情况下,删除一个txt文件的指定部分行,适合在处理大型txt文本文档时使用。

一、项目背景

公司里的电脑只能连接公司的网络,而且浏览记录都会被网安记录下来,就很不利于摸鱼,领导在旁边又很不利于看手机,于是就有了一个想法:把小说下载下来以txt的格式在电脑上看小说,看上去就像在看资料一样,安静摸鱼,岂不美哉!但问题出现了,txt文件看久了就很难保存看到哪里了,下一次打开很难找到上次的位置,于是就想找个办法,解决这个问题。

二、项目实现

一个自然而然的想法,那就是把我所有看过的内容都删掉就可以啦。但看一段删一段很麻烦,影响阅读连贯性;看很久再一下子删很多又很慢很麻烦,像是看了一会儿小说就要等一会儿广告一样,这很不友好。所以何不写一段简单的小代码来实现它。这个小程序应该需要有以下几个主要的子功能:

1.GUI

本人控制台命令几乎完全不会,又用惯了GUI,既然现在要写一个自己的小程序,那总得有个图形化交互界面吧。于是用半天时间自学了一下python的 tkinter 库,以此为基础做一个简单的界面。因此只会在这部分对使用的函数做一个基本介绍。

在这一部分,主要用到的函数有:

import tkinter as tk
from tkinter import filedialog
from tkinter.messagebox import showwarning, showinfo
window=tk.Tk()  # 创建窗口
window.geometry('横x竖')  # 设置窗口大小
tk.Text(window, width=宽度, height=高度, bg='背景颜色', font=('字体', 字号))  # 创建文本框
# tk.Text中的内容可以通过Text.insert('insert','要插入的内容')和
# tk.delete('起始行号.列号','起始行号.列号')进行插入以及删除
tk.Label(window, text='标题的内容')  # 创建标题
tk.Button(window, text='按钮名', command=点击按钮后触发的函数)  # 创建按钮
tk.Checkbutton(window, text="选项名", variable=是否被勾选的信息存储位置,
onvalue=被勾选时的值, offvalue=取消勾选的值, command=勾选后触发的函数)  # 创建勾选按钮
# variable参数的值需要是一个tpye为例如tk.BooleanVar()类型的值
tk.Entry(window,textvariable=用户输入值的存放位置,validate=对用户输入值进行检测的时间,validatecommand=(验证函数,验证函数选项))
showwarning(title='警告的标题',message='警告的具体内容')  # 显示警告
showinfo(title=...,message=...)  # 显示提示
.place(x=...,y=...)  # 设置x,y坐标对界面里的元素进行布局

对于这些函数的太具体用法,这里就不多说了,我也并没有理解所有的参数,只是简单的使用了一下,在用户输入行号的部分会对输入值验证做进一步讨论。

2.读取文件地址

创建一个Button按钮,点击后开始选取文件;常见一个Text窗口,显示读取的文件的绝对路径。这一部分主要学习了这个网页上的操作:
参考网页
增加两个Checkbutton选项,选择对txt文件使用常用的’utf-8’解码或者’gbk’解码方式。

3.选择模式

该小程序需要有两个模式,删除txt文件的部分行后保存为新文件,或者保留txt文件的部分行后保存为新文件。因此需要两个Checkbutton分别对应删除模式和保留模式,而且对于删除和保留这两个模式应该是互斥的,一个被选取时,另一个需要自动取消选取。同时还需要一个Text界面实时展示目前所选取的模式。

另一方面,需要一个Checkbutton,让用户勾选是否使用新文件替换掉原文件,或者将新文件保存为同目录下的一个副本。

4.获取行号

需要Entry文本框来获取用户输入的行号,确定删除/保留第几行到第几行。在这里需要对用户的输入进行验证,是否符合行号的要求:输入的只能是整数,且用负数来表示最后一行,即处理到文章末尾,此处可以通过正则表达式的fullmatch实现。

三、代码

程序的具体实现过程如下,尽力写了些注释orz

"""
create time:2023/9/26
author:Zha_lan
"""
import tkinter as tk
from tkinter import filedialog
from tkinter.messagebox import showwarning, showinfo
import os
import math
import re

file_path = ''  # 存储文件地址
file_text = ''


def open_file():
    """
    打开文件
    :return:local_
    """
    global file_path
    global file_text
    file_path = filedialog.askopenfilename(title=u'选择文件', initialdir=(os.path.expanduser('H:/')))
    print('打开文件:', file_path)
    if file_path is not None:
        text1.delete('1.0', tk.END)
        file_text = "文件路径为:" + file_path
        text1.insert('insert', file_text)


def clear_tx1():
    text1.delete('1.0', tk.END)
    text1.insert('insert', '文件地址:')


window = tk.Tk()  # 创建窗口
window.title('txt裁剪小程序')  # 标题
window.geometry('500x300')  # 窗口尺寸
"""
子功能1:
选取文件,读取文件地址到file_path中
"""
if not os.path.exists('cache.txt'):
    f = open('cache.txt', 'w')
    f.close()
f = open('cache.txt', 'r')
last_file = f.readline().strip()
last_mode = list()
if last_file != '':
    file_path = last_file
    last_mode = [eval(x) for x in f.readline().split()]
text1 = tk.Text(window, width=50, height=5, bg='white', font=('Arial', 12))
text1.insert('insert', '文件地址:')
text1.place(x=23, y=0)

bt0 = tk.Button(window, text='打开文件', width=15, height=2, command=open_file)
bt0.place(x=30, y=100)
bt1 = tk.Button(window, text='清空路径', width=15, height=2, command=clear_tx1)
bt1.place(x=350, y=100)

v_gbk = tk.BooleanVar()
c_gbk = tk.Checkbutton(window, text='gbk编码(非必选)', font=('Arial', 10), variable=v_gbk)
c_gbk.place(x=170, y=120)
"""
子功能2:选择模式,选择删除模式或者保留模式,并且实现两个模式之间的互斥。
"""
mode_text = tk.Text(window, width=20, height=1, bg='gray', font=('Arial', 12))
mode_text.place(x=20, y=160)
mode_text.insert('insert', '选择模式:')
mode_text.config(state='disabled')

mode_var0 = tk.BooleanVar()
mode_var1 = tk.BooleanVar()
mode_var2 = tk.BooleanVar()
# 初始化两个checkbutton
c0 = tk.Checkbutton(window, text="删除", variable=mode_var0)
c1 = tk.Checkbutton(window, text="保留", variable=mode_var1)
c2 = tk.Checkbutton(window, text="是否替换源文件", variable=mode_var2)
c2.place(x=345, y=230)


def display_choice0():
    mode_text.config(state='normal')
    mode_text.delete('1.0', tk.END)
    c1.deselect()
    mode_text.insert('insert', '模式:删除')
    if not mode_var0.get() and not mode_var1.get():
        mode_text.delete('1.0', tk.END)
        mode_text.insert('insert', '选择模式:')
    mode_text.config(state='disabled')


def display_choice1():
    mode_text.config(state='normal')
    mode_text.delete('1.0', tk.END)
    c0.deselect()
    mode_text.insert('insert', '模式:保留')
    if not mode_var0.get() and not mode_var1.get():
        mode_text.delete('1.0', tk.END)
        mode_text.insert('insert', '选择模式:')
    mode_text.config(state='disabled')


c0 = tk.Checkbutton(window, text="删除", variable=mode_var0, onvalue=True, offvalue=False,
                    command=display_choice0, font=('Arial', 12))
c1 = tk.Checkbutton(window, text="保留", variable=mode_var1, onvalue=True, offvalue=False,
                    command=display_choice1, font=('Arial', 12))
c0.place(x=300, y=155)
c1.place(x=400, y=155)
if last_file != '':
    text1.insert('insert', last_file)
    mode_text.config(state='normal')
    mode_text.delete('1.0', tk.END)
    if last_mode[0]:
        c0.select()
        mode_text.insert('insert', '模式:删除')
    else:
        c1.select()
        mode_text.insert('insert', '模式:保留')
    mode_text.config(state='disabled')
    if last_mode[1]:
        c2.select()
"""
子功能3:实现行的选取的选取,只接受数字输入
"""
start = tk.StringVar()
end = tk.StringVar()

pattern = re.compile(r'-?[0-9]*')


def test(content):
    if re.fullmatch(pattern, str(content)) is not None or str(content) == '':
        return True
    else:
        return False


test_dig = window.register(test)
La0 = tk.Label(window, height=1, text='选取行的范围:', font=('Arial', 12))
La0.place(x=20, y=200)
La1 = tk.Label(window, height=1, text='开始:', font=('Arial', 12))
La1.place(x=155, y=200)
start_entry = tk.Entry(window, width=15, textvariable=start, validate='key', validatecommand=(test_dig, '%P'))
start_entry.place(x=200, y=200)
La2 = tk.Label(window, height=1, text='终止:', font=('Arial', 12))
La2.place(x=300, y=200)
end_entry = tk.Entry(window, width=15, textvariable=end, validate='key', validatecommand=(test_dig, '%P'))
end_entry.place(x=345, y=200)
"""
主体功能
"""


def run():
    """
    :mode_var0.get(): 程序模式,True为保留,False为删除
    :file_path: 文件地址
    :start: 起始行
    :end: 终止行
    :return: 一个新的文件
    """
    global start
    global end
    start_var = start.get()
    end_var = end.get()
    if start_var == '' or end_var == '' or file_path == '' or (
            mode_var0.get() == False and mode_var1.get() == False) or last_mode == []:
        showwarning(title='变量不足', message='有变量没有设置,请至少设置文件,模式以及行号')
        return
    if eval(end_var) < 0:
        end_var = math.inf
    else:
        end_var = int(end_var)
    start_var = int(start_var) - 1
    for i in range(10):
        new_filepath = file_path + '(' + str(i) + ').txt'
        if not os.path.exists(new_filepath):
            break
    else:
        showwarning(title='警告', message='该文件所在目录下有太多类似名字的文件')
        return
    if v_gbk.get():
        f = open(file_path, 'r', encoding='gbk')
    else:
        f = open(file_path, 'r', encoding='utf-8')
    new_f = open(new_filepath, 'w', encoding='utf-8')
    line = '~'
    pointer = 0
    while line:
        line = f.readline()
        if mode_var0.get():  # 删除模式
            if pointer < start_var or pointer > end_var:
                new_f.write(line)
            if pointer == start_var:
                new_f.write('\n<删除部分>\n\n')
        if not mode_var0.get():  # 保留模式
            if start_var <= pointer <= end_var:
                new_f.write(line)
            if pointer == start_var - 1 or pointer == end_var + 1:
                new_f.write('\n<删除部分>\n\n')
        pointer += 1
        # 进度显示
        if pointer % 1000 == 0:
            progress_bar.config(state='normal')
            progress_bar.delete('1.0', tk.END)
            sentence = '已处理' + str(pointer) + '行'
            progress_bar.insert('insert', sentence)
    f.close()
    new_f.close()
    if mode_var2.get():
        os.remove(file_path)
        os.rename(new_filepath, file_path)
        showinfo(title='提示', message='文件已处理完成\n新文件为{}'.format(file_path))
    else:
        showinfo(title='提示', message='文件已处理完成\n新文件为{}'.format(new_filepath))
    with open('cache.txt', 'w') as f:
        f.write(file_path + '\n')
        print(file_path + '\n')
        f.write('\t'.join([str(mode_var0.get()), str(mode_var1.get())]))
        print('\t'.join([str(mode_var0.get()), str(mode_var1.get())]))
        f.close()


RUN = tk.Button(window, text='运行', width=15, height=2, command=run, font=('SimHei', 15))
RUN.place(x=100, y=230)
progress_bar = tk.Text(window, width=20, height=1, bg='gray', font=('Arial', 12))
progress_bar.insert('insert', '已处理0行')
progress_bar.place(x=280, y=260)
progress_bar.config(state='disabled')
window.mainloop()  # 显示

四、总结

最后用pyinstaller打包了一个小程序也和源代码一起附在博客资源里了。太久没写代码了,找个地方记录一下自己的上班摸鱼coding生活~~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值