python实现文件搜索工具(简易版)
在python学习过程中有一次需要进行GUI 的绘制,
而在python中有自带的库tkinter可以用来简单的GUI编写,于是转而学习tkinter库的使用。
学以致用,现在试着编写一个简单的磁文件搜索工具,
方法就是将指定的文件夹进行扫描遍历,把其中的每个文件路径数据存入数据库,
然后使用数据库搜索文件就很快捷。实现的效果大致如下:
整个程序分为大致几个模块:
主界面的绘制,
指定文件夹功能函数,
搜索文件功能函数,
ui线程与扫描线程同步函数,
扫描线程工作函数
要实现扫描文件功能时,
导入了一个这样的模块 disk.py
这个模块实现的功能就是将指定文件夹下的所有文件遍历,并将路径和所在盘符存到一个列表中返回
import os
import os.path as pt def scan_file(path):
result = []
for root, dirs, files in os.walk(path):
for f in files:
file_path = pt.abspath(pt.join(root, f)) result.append((file_path, file_path[0])) # 保存路径与盘符 return result
然后我们需要将扫描到的文件存入到数据库中,
因此需要编写数据库模块 datebase.py
import sqlite3 class DataMgr: def __init__(self):
# 创建或打开一个数据库
# check_same_thread 属性用来规避多线程操作数据库的问题
self.conn = sqlite3.connect("file.db", check_same_thread=False)
# 建表
self.conn.execute('create table if not exists disk_table('
'id integer primary key autoincrement,'
'file_path text,'
'drive_letter text)')
# 创建索引 用来提高搜索速度
self.conn.execute('create index if not exists index_path on disk_table(file_path)') # 批量插入数据
def batch_insert(self, data):
for line in data:
self.conn.execute('insert into disk_table values (null,?,?)', line)
self.conn.commit() # 模糊搜索
def query(self, key):
cursor = self.conn.cursor()
cursor.execute("select file_path from disk_table where file_path like ?", ('%{0}%'.format(key),))
r = [row[0] for row in cursor]
cursor.close()
return r def close(self):
self.conn.close()
还需要一个额外的模块为 progressbar.py
这个模块的功能是在扫描时弹出一个进度条窗口,
使得GUI功能看起来更完善
from tkinter import *
from tkinter import ttk class GressBar: def start(self):
top = Toplevel() # 弹出式窗口,实现多窗口时经常用到
self.master = top
top.overrideredirect(True) # 去除窗体的边框
top.title("进度条")
Label(top, text="正在扫描选定路径的文件,请稍等……", fg="blue").pack(pady=2)
prog = ttk.Progressbar(top, mode='indeterminate', length=200) # 创建进度条
prog.pack(pady=10, padx=35)
prog.start() top.resizable(False, False) # 参数为false表示不允许改变窗口尺寸
top.update()
# 计算窗口大小,使显示在屏幕中央
curWidth = top.winfo_width()
curHeight = top.winfo_height()
scnWidth, scnHeight = top.maxsize()
tmpcnf = '+%d+%d' % ((scnWidth - curWidth) / 2, (scnHeight - curHeight) / 2)
top.geometry(tmpcnf)
top.mainloop() def quit(self):
if self.master:
self.master.destroy()
主体的search.py 代码:
from tkinter import *
from tkinter import ttk
import tkinter.filedialog as dir
import queue
import threading
import progressbar
import disk
from database import DataMgr class SearchUI: def __init__(self):
# 创建一个消息队列
self.notify_queue = queue.Queue()
root = Tk()
self.master = root
self.create_menu(root)
self.create_content(root)
self.path = 'D:'
root.title('the search tool')
root.update()
# 在屏幕中心显示窗体
curWidth = root.winfo_width()
curHeight = root.winfo_height()
scnWidth, scnHeight = root.maxsize() # 得到屏幕的宽度和高度
tmpcnf = '+%d+%d' % ((scnWidth - curWidth)/2, (scnHeight-curHeight)/2)
root.geometry(tmpcnf) # 创建一个进度条对话框实例
self.gress_bar = progressbar.GressBar() # 创建一个数据库的实例
self.data_mgr = DataMgr() # 在UI线程启动消息队列循环
self.process_msg()
root.mainloop() # ui线程与扫描线程同步
def process_msg(self):
# after方法,相当于一个定时器,
# 第一个参数是时间的毫秒值,
# 第二个参数指定执行一个函数
self.master.after(400, self.process_msg)
# 这样我们就在主线程建立了一个消息队列,
# 每隔一段时间去消息队列里看看,
# 有没有什么消息是需要主线程去做的,
# 有一点需要特别注意,
# 主线程消息队列里也不要干耗时操作,
# 该队列仅仅用来更新UI。
while not self.notify_queue.empty():
try:
msg = self.notify_queue.get()
if msg[0] == 1:
self.gress_bar.quit() except queue.Empty:
pass # 扫描线程工作
def execute_asyn(self):
# 定义一个scan函数,放入线程中去执行耗时扫描
def scan(_queue):
if self.path:
paths = disk.scan_file(self.path) # 位于disk.py
self.data_mgr.batch_insert(paths) # 位于database.py _queue.put((1,))
th = threading.Thread(target=scan, args=(self.notify_queue,))
th.setDaemon(True) # 设置为守护进程
th.start() self.gress_bar.start() # 菜单绘制
def create_menu(self, root):
menu = Menu(root) # 创建菜单 # 二级菜单
file_menu = Menu(menu, tearoff=0)
file_menu.add_command(label='设置路径', command=self.open_dir)
file_menu.add_separator()
file_menu.add_command(label='扫描', command=self.execute_asyn) about_menu = Menu(menu, tearoff=0)
about_menu.add_command(label='version1.0') # 在菜单栏中添加菜单
menu.add_cascade(label='文件', menu=file_menu)
menu.add_cascade(label='关于', menu=about_menu)
root['menu'] = menu # 主界面绘制
def create_content(self, root):
lf = ttk.LabelFrame(root, text='文件搜索')
lf.pack(fill=X, padx=15, pady=8) top_frame = Frame(lf)
top_frame.pack(fill=X, expand=YES, side=TOP, padx=15, pady=8) self.search_key = StringVar()
ttk.Entry(top_frame, textvariable=self.search_key, width=50).pack(fill=X, expand=YES, side=LEFT)
ttk.Button(top_frame, text="搜索", command=self.search_file).pack(padx=15, fill=X, expand=YES) bottom_frame = Frame(lf)
bottom_frame.pack(fill=BOTH, expand=YES, side=TOP, padx=15, pady=8) band = Frame(bottom_frame)
band.pack(fill=BOTH, expand=YES, side=TOP) self.list_val = StringVar()
listbox = Listbox(band, listvariable=self.list_val, height=18)
listbox.pack(side=LEFT, fill=X, expand=YES) vertical_bar = ttk.Scrollbar(band, orient=VERTICAL, command=listbox.yview)
vertical_bar.pack(side=RIGHT, fill=Y)
listbox['yscrollcommand'] = vertical_bar.set horizontal_bar = ttk.Scrollbar(bottom_frame, orient=HORIZONTAL, command=listbox.xview)
horizontal_bar.pack(side=BOTTOM, fill=X)
listbox['xscrollcommand'] = horizontal_bar.set # 给list动态设置数据,set方法传入一个元组
self.list_val.set(('等待搜索',)) # 搜索文件
def search_file(self):
if self.search_key.get():
result_data = self.data_mgr.query(self.search_key.get())
if result_data:
self.list_val.set(tuple(result_data)) # 指定文件夹
def open_dir(self):
d = dir.Directory()
self.path = d.show(initialdir=self.path) if __name__ == '__main__':
SearchUI()
问题总结:
1.UI线程负责界面的绘制与更新,如果在UI线程中进行耗时操作,会影响界面的流畅性,所以需要异步线程。
此时的问题在于UI的主线程与异步线程的通信问题,为什么一定要两个线程通信?
因为在大多数GUI界面编程中,异步线程都是不能对当前界面进行操作更新的,否则会引起界面混乱。
可以简单的理解成 如果异步线程也操作主界面,则两个线程对相同资源进行操作,就会导致混乱。
接下来的问题是tkinter中没有提供接口进行线程通信,因此我们通过消息队列的方式来同步线程,用到的类为Queue。
项目中当在消息队列中检索到消息为元组(1, )时,说明子线程(扫描)已经结束了,告知主线程可以结束子线程了。
2.扫描文件夹时需要将所选文件夹中的所有文件遍历一遍,发现python中提供了方法os.walk(path), 可以直接达到这一效果,所以说python在写代码时确实提供了方便。
3.该磁盘搜索工具用到的原理是将文件路径存到数据库中,再进行检索。 选用的数据库为sqlite,已经可以满足该项目的要求。在主线程创建数据库,子线程操作数据库,有可能出现问题,因此设置check_same_thread = false 来拒绝多线程的访问。
4.在进行GUI编程时,打算在扫描等待时添加一个进度条显示窗口,也就需要多窗口,用到了toplevel,表现为一个弹出式窗口,在使用toplevel时,要注意首先需要一个根窗口。
python实现文件搜索工具(简易版)的更多相关文章
- 用python制作文件搜索工具,深挖电脑里的【学习大全】
咳咳~懂得都懂啊 点击此处找管理员小姐姐领取正经资料~ 开发环境 解释器: Python 3.8.8 | Anaconda, Inc. 编辑器: pycharm 专业版 先演示效果 开始代码,先导入模 ...
- Atitit.文件搜索工具 attilax 总结
Atitit.文件搜索工具 attilax 总结 1. 指定目录按照体积大小精确搜索1 1.1. File Seeker 4.5 版本的可以,3.5版本的不行..1 2. 按照文件内容搜索1 2.1. ...
- 文件搜索工具everything
Everything是voidtools开发的一款文件搜索工具,官网描述为“基于名称实时定位文件和目录(Locate files and folders by name instantly) (“Ev ...
- Android之文件搜索工具类
/** * @detail 搜索sdcard文件 * @param 需要进行文件搜索的目录 * @param 过滤搜索文件类型 * */ private void search(File file, ...
- python 检索文件内容工具
公司内部需求一个工具检索目录下的文件在另外的目录中使用次数, 用来优化包体的大小. 此代码效率并不高效, 另添加对应的 后缀检索. 用python 实现比较快速, 另还有缺点是只支持 utf-8 格式 ...
- DocFetcher 本机文件搜索工具
优点: 支持的文件类型多 全文搜索 可以随时update索引
- Python并发编程-多进程socketserver简易版
普通版的socketserver #server.py import socket sk = socket.socket() sk.bind(('127.0.0.1',8080))#建立连接 sk.l ...
- Ubuntu 16.04下轻量级文件搜索工具Catfish
Catfish搜索文件速度快,但是不支持正则表达式. 安装: sudo add-apt-repository ppa:catfish-search/ppa sudo apt-get update su ...
- servlet 文件上传简易版
package cn.itcast.servlet; import java.io.File;import java.io.IOException;import java.util.List; imp ...
随机推荐
- Layui + 微信小程序 + PC端 + GatewayWorker + 移动端即时聊天系统
- 4)抽象方法不能为private,final或者static,为什么?
抽象方法的最实质的意 义在于被未来的子类覆盖实现掉.它自己是个空方法.private的实质意义在于本类其他方法调用它.你自己是个空方法,别人调用你有什么用?所以 abstract和private在一起 ...
- OpenJudge计算概论-找出第k大的数
/*================================================ 找出第k大的数 总时间限制: 1000ms 内存限制: 1000kB 描述 用户输入N和K,然后接 ...
- Somatic hypermutation (or SHM) is a cellular mechanism by which the immune system adapts to the new foreign elements that confront it (e.g. microbes), as seen during class switching. Somatic hypermut
Somatic hypermutation (or SHM) is a cellular mechanism by which the immune system adapts to the new ...
- web框架性能点
awesome-go-web-frameworks/README.md at master · speedwheel/awesome-go-web-frameworkshttps://github.c ...
- android: Canvas的drawArc()方法的几个误区
绘制圆环很多时候会用到Canvas的drawArc方法, drawArc()方法的说明很简单: public void drawArc (RectF oval, float startAngle, f ...
- JAVA_SWT 事件的四种写法
一:匿名内部类写法 在一个组件下加入以下语句 text.addMouseListener(new MouseAdapter(){ public void mouseDoubleClich(MouseE ...
- 使用docker运行mysql
以前开发的时候都是用本地的sqlite开发,但是极少数情况下,sqlite支持的语法发布到服务器上链接mysql会报错. 为了避免这种现象,还是链接本地mysql开发还是更稳定的, 可是开发的项目多了 ...
- 自己发挥的内容 有关ViewModel的一句翻译(难点expecting)
- 123457123457#0#-----com.cym.shuXueWangGuo1--前拼后广--儿童数学
123456123456#1#-----com.cym.shuXueWangGuo1--前拼后广--儿童数学