在日常的开发和办公中,查找和筛选特定文件或文件内容的需求十分常见。尤其是在处理大量文件时,如何高效地搜索到所需内容,往往是一个挑战。今天,我们将分享一个基于pyqt6的文件搜索工具,它不仅能根据文件后缀进行搜索,还支持关键字匹配和详细信息展示。通过这款工具,您可以更高效地管理和查找文件,提升工作效率。
1.概述
这款基于pyqt6开发的文件搜索工具,具备文件搜索、文件内容匹配、搜索结果显示及导出功能。程序支持通过拖拽输入框选择目标文件夹,并对其中的文件进行后缀筛选和内容搜索。匹配结果可以实时显示在界面上,用户可以查看匹配的文件名、大小以及路径,还可以导出搜索结果或者树结构信息。
该工具的核心特点包括:
- 多线程搜索:使用pyqt的qthread进行文件搜索操作,避免ui卡顿。
- 多种文件编码支持:支持utf-8、gbk等常见编码格式,能够准确读取文件内容。
- 实时展示匹配结果:搜索结果实时更新,方便用户查看和处理。
- 导入导出功能:支持导入文件树信息及导出搜索结果。
2.功能使用
2.1 文件夹选择与搜索设置
启动应用后,用户首先需要选择一个目标文件夹,输入需要搜索的文件后缀以及关键词。文件后缀支持通过分号分隔的多项选择,例如 .txt; .log,而关键词则是搜索文件内容时所需要匹配的字符串。
界面上设计了拖拽功能,用户可以直接将目标文件夹拖拽到输入框中,免去手动输入路径的麻烦。
2.2 文件内容搜索
输入完搜索条件后,点击“开始搜索”按钮,程序会启动一个后台线程(searchworker),在目标文件夹中递归查找符合条件的文件。搜索的内容不仅是文件名匹配,还包括文件内部的内容是否包含指定的关键字。程序采用了多种编码方式进行文件内容的读取,以确保文件格式不同的情况下能够正确处理。
搜索过程中,ui界面并不会卡顿,用户可以继续操作其他功能。匹配到的文件会实时显示在左侧的文件树结构中,每一项显示文件名、大小和路径等信息。
2.3 单文件搜索
除了支持批量文件搜索外,程序还提供了单文件搜索的功能。在“单文件搜索”板块中,用户可以选择指定的文件,输入要查找的关键词,点击搜索按钮后,程序会展示匹配到的行及其内容,并在右侧的文本框中显示完整匹配结果。
该功能可以帮助开发人员快速定位特定代码段或文件中的问题,极大提高了工作效率。
2.4 结果展示与导出
搜索结束后,程序会统计文件总数、匹配的行数和搜索所花费的时间,展示在界面上。同时,所有匹配到的结果会以列表的形式显示在右侧,用户可以选择具体的行进行查看。
如果用户需要保存搜索结果,程序提供了导出功能。用户可以将所有匹配的文件内容导出为文本文件,方便后续查看或共享。另外,文件树的结构信息也可以导出,便于存档和分析。
2.5 文件树信息导入
程序还支持导入文件树信息。用户可以将之前导出的树结构文件导入到工具中,快速恢复之前的搜索状态和文件结构。导入后的文件树可以继续进行搜索或查看,提升了工具的灵活性和可用性。
3.代码结构与实现
3.1 多线程处理
为了保证ui界面的流畅性,程序使用了pyqt的qthread来处理搜索任务。具体来说,searchworker类负责遍历目标文件夹并查找符合条件的文件,而filereader类则负责读取文件内容并检查是否包含指定的关键字。多线程的使用有效避免了ui在进行文件搜索时的卡顿问题,让用户能够在后台进行其他操作。
class searchworker(qthread):
update_file = pyqtsignal(dict)
finished = pyqtsignal()
def __init__(self, folder, extensions, keyword):
super().__init__()
self.folder = folder
self.extensions = extensions
self.keyword = keyword
def run(self):
# 搜索文件并匹配关键字
pass
3.2 文件编码支持
文件内容的读取采用了多种常见编码格式(如utf-8、gbk等),以确保无论文件是用哪种编码保存,程序都能正确地读取其内容。在filereader类中,我们尝试了不同的编码方式,直到成功读取文件为止。
def file_contains_keyword(self, path, keyword):
for encoding in [‘utf-8’, ‘gbk’, ‘latin-1’]:
try:
with open(path, ‘r’, encoding=encoding) as f:
return any(keyword in line for line in f)
except (unicodedecodeerror, exception):
continue
return false
3.3 导入导出功能
导出功能实现了将文件匹配结果和文件树结构导出为文本文件,支持用户将数据保存在指定路径。通过qfiledialog对话框,用户可以选择保存的位置或导入的数据文件。
def export_match_list(self):
# 导出匹配结果
pass
def import_tree_info(self):
# 导入文件树
pass
4.效果展示

5.相关源码
import sys
import os
import time
from pyqt6.qtwidgets import (
qapplication, qmainwindow, qwidget, qvboxlayout, qhboxlayout, qgridlayout,
qlabel, qlineedit, qpushbutton, qtreewidget, qtreewidgetitem,
qlistwidget, qtextedit, qscrollarea, qfiledialog, qmessagebox
)
from pyqt6.qtcore import qt, qthread, pyqtsignal, qmimedata, qsize
from pyqt6.qtgui import qdragenterevent, qdropevent
class draggablelineedit(qlineedit):
def __init__(self, parent=none):
super().__init__(parent)
self.setacceptdrops(true)
def dragenterevent(self, event: qdragenterevent):
if event.mimedata().hasurls():
event.acceptproposedaction()
def dropevent(self, event: qdropevent):
urls = event.mimedata().urls()
if urls:
path = urls[0].tolocalfile()
if os.path.isdir(path) or os.path.isfile(path):
self.settext(path)
class searchworker(qthread):
update_file = pyqtsignal(dict)
finished = pyqtsignal()
def __init__(self, folder, extensions, keyword):
super().__init__()
self.folder = folder
self.extensions = extensions
self.keyword = keyword
self.running = true
def run(self):
for root, _, files in os.walk(self.folder):
if not self.running: break
for file in files:
if any(file.endswith(ext) for ext in self.extensions):
path = os.path.join(root, file)
if self.file_contains_keyword(path, self.keyword):
size = os.path.getsize(path)
self.update_file.emit({
"name": file,
"size": self.format_size(size),
"path": path
})
self.finished.emit()
def file_contains_keyword(self, path, keyword):
for encoding in ['utf-8', 'gbk', 'latin-1']:
try:
with open(path, 'r', encoding=encoding) as f:
return any(keyword in line for line in f)
except (unicodedecodeerror, exception):
continue
return false
def format_size(self, size):
if size < 1024:
return f"{size} b"
elif size < 1024 * 1024:
return f"{size/1024:.1f} kb"
else:
return f"{size/(1024 * 1024):.1f} mb"
class filereader(qthread):
update_line = pyqtsignal(str)
finished = pyqtsignal()
def __init__(self, path, keyword):
super().__init__()
self.path = path
self.keyword = keyword
self.results = []
def run(self):
self.results = []
for encoding in ['utf-8', 'gbk', 'latin-1']:
try:
with open(self.path, 'r', encoding=encoding) as f:
for i, line in enumerate(f, 1):
if self.keyword in line:
text = f"line {i}: {line.strip()[:50]}"
self.update_line.emit(text)
self.results.append(line.strip())
break
except (unicodedecodeerror, exception):
continue
self.finished.emit()
class allfilesreader(qthread):
update_line = pyqtsignal(str)
finished = pyqtsignal()
def __init__(self, paths, keyword):
super().__init__()
self.paths = paths
self.keyword = keyword
def run(self):
for path in self.paths:
if not os.path.isfile(path):
continue
reader = filereader(path, self.keyword)
reader.update_line.connect(self.update_line.emit)
reader.start()
reader.wait()
self.finished.emit()
class mainwindow(qmainwindow):
def __init__(self):
super().__init__()
self.current_matches = []
self.search_thread = none
self.file_reader = none
self.start_time = 0
self.init_ui()
def init_ui(self):
self.setwindowtitle("pyqt6文件搜索工具")
self.setgeometry(100, 100, 1200, 700)
main_widget = qwidget()
self.setcentralwidget(main_widget)
main_layout = qvboxlayout(main_widget)
# 顶部控制面板
top_panel = qwidget()
top_layout = qgridlayout(top_panel)
self.folder_input = draggablelineedit()
self.ext_input = qlineedit(".txt")
self.keyword_input = qlineedit()
self.btn_search = qpushbutton("开始搜索")
self.btn_search.clicked.connect(self.start_search)
top_layout.addwidget(qlabel("目标文件夹:"), 0, 0)
top_layout.addwidget(self.folder_input, 0, 1)
top_layout.addwidget(self.create_browse_btn(), 0, 2)
top_layout.addwidget(qlabel("文件后缀:"), 1, 0)
top_layout.addwidget(self.ext_input, 1, 1)
top_layout.addwidget(qlabel("搜索内容:"), 2, 0)
top_layout.addwidget(self.keyword_input, 2, 1)
top_layout.addwidget(self.btn_search, 0, 3, 3, 1)
# 统计信息
stats_panel = qwidget()
stats_layout = qhboxlayout(stats_panel)
self.file_count = qlabel("文件总数: 0")
self.line_count = qlabel("匹配行数: 0")
self.time_label = qlabel("耗时: 0.00秒")
stats_layout.addwidget(self.file_count)
stats_layout.addwidget(self.line_count)
stats_layout.addwidget(self.time_label)
# 主内容区域
content_panel = qwidget()
content_layout = qhboxlayout(content_panel)
# 左侧结果树
self.tree = qtreewidget()
self.tree.setheaderlabels(["文件名", "大小", "路径"])
self.tree.setcolumnwidth(0, 250)
self.tree.setcolumnwidth(1, 100)
self.tree.doubleclicked.connect(self.on_tree_double_click)
tree_scroll = qscrollarea()
tree_scroll.setwidgetresizable(true)
tree_scroll.setwidget(self.tree)
# 右侧面板
right_panel = qwidget()
right_layout = qvboxlayout(right_panel)
# 按钮面板
btn_panel = qwidget()
btn_layout = qhboxlayout(btn_panel)
self.btn_search_all = qpushbutton("搜索所有文件")
self.btn_export_matches = qpushbutton("导出结果")
self.btn_export_tree = qpushbutton("导出树信息")
self.btn_import_tree = qpushbutton("导入树信息")
btn_layout.addwidget(self.btn_search_all)
btn_layout.addwidget(self.btn_export_matches)
btn_layout.addwidget(self.btn_export_tree)
btn_layout.addwidget(self.btn_import_tree)
# 单文件搜索
single_panel = qwidget()
single_layout = qhboxlayout(single_panel)
self.single_input = draggablelineedit()
btn_single = qpushbutton("搜索")
btn_single.clicked.connect(self.single_file_search)
single_layout.addwidget(qlabel("单文件搜索:"))
single_layout.addwidget(self.single_input)
single_layout.addwidget(btn_single)
# 匹配列表
self.match_list = qlistwidget()
self.match_list.doubleclicked.connect(self.on_list_double_click)
list_scroll = qscrollarea()
list_scroll.setwidgetresizable(true)
list_scroll.setwidget(self.match_list)
# 详情文本框
self.detail_text = qtextedit()
self.detail_text.setreadonly(true)
right_layout.addwidget(single_panel)
right_layout.addwidget(btn_panel)
right_layout.addwidget(list_scroll)
right_layout.addwidget(self.detail_text)
content_layout.addwidget(tree_scroll)
content_layout.addwidget(right_panel)
main_layout.addwidget(top_panel)
main_layout.addwidget(stats_panel)
main_layout.addwidget(content_panel)
# 连接新按钮信号
self.btn_search_all.clicked.connect(self.search_all_files)
self.btn_export_matches.clicked.connect(self.export_match_list)
self.btn_export_tree.clicked.connect(self.export_tree_info)
self.btn_import_tree.clicked.connect(self.import_tree_info)
def create_browse_btn(self):
btn = qpushbutton("浏览")
btn.clicked.connect(self.browse_folder)
btn.setfixedsize(qsize(80, 30))
return btn
def browse_folder(self):
path = qfiledialog.getexistingdirectory(self, "选择文件夹")
if path:
self.folder_input.settext(path)
def start_search(self):
if self.search_thread and self.search_thread.isrunning():
return
folder = self.folder_input.text()
exts = self.ext_input.text().strip().split(";")
keyword = self.keyword_input.text().strip()
if not all([folder, exts, keyword]):
qmessagebox.critical(self, "错误", "请填写所有搜索条件")
return
self.tree.clear()
self.match_list.clear()
self.current_matches = []
self.update_counts()
self.start_time = time.time()
self.search_thread = searchworker(folder, exts, keyword)
self.search_thread.update_file.connect(self.add_file_result)
self.search_thread.finished.connect(self.on_search_finished)
self.search_thread.start()
self.btn_search.setenabled(false)
def add_file_result(self, data):
item = qtreewidgetitem()
item.settext(0, data["name"])
item.settext(1, data["size"])
item.settext(2, data["path"])
self.tree.addtoplevelitem(item)
self.file_count.settext(f"文件总数: {self.tree.toplevelitemcount()}")
def on_search_finished(self):
self.btn_search.setenabled(true)
elapsed = time.time() - self.start_time
self.time_label.settext(f"耗时: {elapsed:.2f}秒")
def single_file_search(self):
path = self.single_input.text()
keyword = self.keyword_input.text().strip()
if not os.path.isfile(path):
qmessagebox.critical(self, "错误", "无效的文件路径")
return
self.match_list.clear()
self.current_matches = []
self.file_reader = filereader(path, keyword)
self.file_reader.update_line.connect(self.match_list.additem)
self.file_reader.finished.connect(lambda: (
self.line_count.settext(f"匹配行数: {self.match_list.count()}"),
self.current_matches.extend(self.file_reader.results)
))
self.file_reader.start()
def on_tree_double_click(self):
item = self.tree.currentitem()
if not item: return
path = item.text(2)
keyword = self.keyword_input.text().strip()
self.match_list.clear()
self.current_matches = []
self.file_reader = filereader(path, keyword)
self.file_reader.update_line.connect(self.match_list.additem)
self.file_reader.finished.connect(lambda: (
self.line_count.settext(f"匹配行数: {self.match_list.count()}"),
self.current_matches.extend(self.file_reader.results)
))
self.file_reader.start()
def on_list_double_click(self):
index = self.match_list.currentrow()
if 0 <= index < len(self.current_matches):
self.detail_text.setplaintext(self.current_matches[index])
def search_all_files(self):
paths = []
root = self.tree.invisiblerootitem()
for i in range(root.childcount()):
item = root.child(i)
paths.append(item.text(2))
keyword = self.keyword_input.text().strip()
if not keyword:
qmessagebox.critical(self, "错误", "请输入搜索关键字")
return
self.match_list.clear()
self.current_matches = []
self.all_files_reader = allfilesreader(paths, keyword)
self.all_files_reader.update_line.connect(self.match_list.additem)
self.all_files_reader.finished.connect(lambda: (
self.line_count.settext(f"匹配行数: {self.match_list.count()}"),
self.current_matches.extend(self.file_reader.results) if self.file_reader else none
))
self.all_files_reader.start()
def export_match_list(self):
keyword = self.keyword_input.text().strip() or "search"
timestamp = time.strftime("%y%m%d_%h%m%s")
filename = f"{keyword}_{timestamp}.txt"
desktop = f"d:/桌面/"
path = os.path.join(desktop, filename)
with open(path, 'w', encoding='utf-8') as f:
for i in range(self.match_list.count()):
f.write(self.match_list.item(i).text() + "\n")
qmessagebox.information(self, "导出完成", f"文件已保存到:{path}")
def export_tree_info(self):
items = []
root = self.tree.invisiblerootitem()
for i in range(root.childcount()):
item = root.child(i)
items.append("\t".join([
item.text(0),
item.text(1),
item.text(2)
]))
timestamp = time.strftime("%y%m%d_%h%m%s")
filename = f"tree_export_{timestamp}.txt"
desktop = f"d:/桌面/"
path = os.path.join(desktop, filename)
with open(path, 'w', encoding='utf-8') as f:
f.write("\n".join(items))
qmessagebox.information(self, "导出完成", f"树结构已保存到:{path}")
def import_tree_info(self):
path, _ = qfiledialog.getopenfilename(self, "选择导入文件", "", "文本文件 (*.txt)")
if not path:
return
self.tree.clear()
with open(path, 'r', encoding='utf-8') as f:
for line in f:
parts = line.strip().split('\t')
if len(parts) != 3:
continue
item = qtreewidgetitem()
item.settext(0, parts[0])
item.settext(1, parts[1])
item.settext(2, parts[2])
self.tree.addtoplevelitem(item)
self.file_count.settext(f"文件总数: {self.tree.toplevelitemcount()}")
def update_counts(self):
self.file_count.settext(f"文件总数: {self.tree.toplevelitemcount()}")
self.line_count.settext(f"匹配行数: {self.match_list.count()}")
if __name__ == "__main__":
app = qapplication(sys.argv)
window = mainwindow()
window.show()
sys.exit(app.exec())
6.总结
这是一个基于pyqt6开发的文件搜索与内容匹配工具。该工具不仅支持高效的文件查找,还能通过关键字匹配文件内容,并提供详细的匹配结果展示和导出功能。利用pyqt的多线程特性,程序在执行搜索任务时能够保持界面的响应性,大大提升了用户体验。
未来的改进方向包括:
增强搜索效率:可以增加更多的文件搜索选项,如模糊匹配或正则表达式支持,以满足更复杂的查找需求。
界面优化:加入进度条或其他ui元素,实时反馈搜索进度。
功能扩展:支持更多的文件操作,如批量重命名、批量删除等。
以上就是基于python开发高效文件搜索与内容匹配工具的详细内容,更多关于python文件搜索与内容匹配的资料请关注代码网其它相关文章!
发表评论