everything的确是一个很好的工具,所以仿照开发一个
# -*- coding: utf-8 -*-import sys
import os
import sqlite3
import threading
from pathlib import Path
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,QPushButton, QLineEdit, QListWidget, QLabel, QCheckBox, QProgressBar,QMessageBox, QGridLayout, QGroupBox
)
from PyQt5.QtCore import Qt, pyqtSignal, QObjectclass IndexerSignals(QObject):count_update = pyqtSignal(int) status = pyqtSignal(str) current_file = pyqtSignal(str) finished = pyqtSignal(int) error = pyqtSignal(str)class FileIndexer(QObject):def __init__(self):super().__init__()self.signals = IndexerSignals()self.stop_requested = Falsedef stop(self):self.stop_requested = Truedef build_index(self, roots):db_path = Path.home() / ".file_searcher_db.sqlite"# 删除旧库,确保干净重建if db_path.exists():try:os.remove(db_path)except:passtry:conn = sqlite3.connect(str(db_path))cur = conn.cursor()cur.execute("""CREATE VIRTUAL TABLE IF NOT EXISTS files USING fts5(name, path UNINDEXED, -- UNINDEXED 表示该字段存储但不参与全文索引(节省空间且避免误搜)is_dir UNINDEXED,tokenize = 'unicode61');""")conn.commit()processed_count = 0batch = []self.signals.status.emit("正在极速扫描中...")for root in roots:if self.stop_requested:breakfor dirpath, dirnames, filenames in os.walk(root):if self.stop_requested:break# 1. 处理文件夹for dname in dirnames:full_path = os.path.join(dirpath, dname)batch.append((dname, full_path, 1)) # 1 代表文件夹processed_count += 1# 2. 处理文件for fname in filenames:full_path = os.path.join(dirpath, fname)batch.append((fname, full_path, 0)) # 0 代表文件processed_count += 1if len(batch) >= 2000:cur.executemany("INSERT INTO files(name, path, is_dir) VALUES (?, ?, ?)", batch)conn.commit()batch.clear()# 发送信号更新界面self.signals.count_update.emit(processed_count)self.signals.current_file.emit(dirpath) # 显示当前扫描到的目录即可# 写入剩余的数据if batch:cur.executemany("INSERT INTO files(name, path, is_dir) VALUES (?, ?, ?)", batch)conn.commit()conn.close()if not self.stop_requested:self.signals.finished.emit(processed_count)else:self.signals.status.emit("索引已停止")except Exception as e:self.signals.error.emit(str(e))class MainWindow(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("极速文件搜索器 v3.0 (逻辑修正版)")self.setGeometry(200, 100, 950, 700)self.db_path = Path.home() / ".file_searcher_db.sqlite"self.indexer = FileIndexer()self.thread = Noneself.init_ui()def init_ui(self):central = QWidget()self.setCentralWidget(central)layout = QVBoxLayout(central)# === 1. 盘符选择 ===group_disk = QGroupBox("1. 索引设置")h_disk = QHBoxLayout()self.drives_layout = QGridLayout()self.checkboxes = {}self.refresh_drives() # 初始化盘符btn_refresh = QPushButton("刷新盘符")btn_refresh.clicked.connect(self.refresh_drives)h_disk.addLayout(self.drives_layout)h_disk.addWidget(btn_refresh)group_disk.setLayout(h_disk)# === 2. 控制台与进度 ===group_ctrl = QGroupBox("2. 索引控制")v_ctrl = QVBoxLayout()h_btns = QHBoxLayout()self.btn_update = QPushButton("开始建立索引")self.btn_update.setStyleSheet("background-color: #2E7D32; color: white; font-weight: bold; padding: 8px;")self.btn_update.clicked.connect(self.start_indexing)self.btn_stop = QPushButton("停止")self.btn_stop.setStyleSheet("background-color: #C62828; color: white; padding: 8px;")self.btn_stop.clicked.connect(self.stop_indexing)self.btn_stop.setEnabled(False)h_btns.addWidget(self.btn_update)h_btns.addWidget(self.btn_stop)self.lbl_status = QLabel("准备就绪")self.progress_bar = QProgressBar()self.progress_bar.setTextVisible(False) # 不显示百分比,因为没有总数self.progress_bar.setRange(0, 0) self.progress_bar.hide()self.lbl_count = QLabel("已收录: 0")self.lbl_current = QLabel("...")self.lbl_current.setStyleSheet("color: gray; font-size: 10px;")v_ctrl.addLayout(h_btns)v_ctrl.addWidget(self.lbl_status)v_ctrl.addWidget(self.progress_bar)h_info = QHBoxLayout()h_info.addWidget(self.lbl_count)h_info.addWidget(self.lbl_current)v_ctrl.addLayout(h_info)group_ctrl.setLayout(v_ctrl)# === 3. 搜索区域 ===group_search = QGroupBox("3. 极速搜索")v_search = QVBoxLayout()h_filter = QHBoxLayout()self.chk_search_file = QCheckBox("搜文件")self.chk_search_file.setChecked(True)self.chk_search_file.stateChanged.connect(lambda: self.on_search(self.edit_search.text()))self.chk_search_dir = QCheckBox("搜文件夹")self.chk_search_dir.setChecked(True)self.chk_search_dir.stateChanged.connect(lambda: self.on_search(self.edit_search.text()))h_filter.addWidget(QLabel("过滤类型:"))h_filter.addWidget(self.chk_search_file)h_filter.addWidget(self.chk_search_dir)h_filter.addStretch()self.edit_search = QLineEdit()self.edit_search.setPlaceholderText("输入文件名关键词(空格隔开表示“与”,例如:合同 2024)...")self.edit_search.setStyleSheet("font-size: 14pt; padding: 6px;")self.edit_search.textChanged.connect(self.on_search)self.list_result = QListWidget()self.list_result.setStyleSheet("font-size: 11pt;")self.list_result.itemDoubleClicked.connect(self.open_item)v_search.addLayout(h_filter)v_search.addWidget(self.edit_search)v_search.addWidget(self.list_result)group_search.setLayout(v_search)# 添加到主布局layout.addWidget(group_disk)layout.addWidget(group_ctrl)layout.addWidget(group_search)def get_drives(self):drives = []for letter in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":d = f"{letter}:\\"if os.path.exists(d):drives.append(d)return drivesdef refresh_drives(self):# 清除旧的for i in reversed(range(self.drives_layout.count())): self.drives_layout.itemAt(i).widget().setParent(None)self.checkboxes.clear()drives = self.get_drives()for i, d in enumerate(drives):cb = QCheckBox(d)# 默认只勾选 C 和 D,避免全部勾选太慢if d.startswith("C") or d.startswith("D"):cb.setChecked(True)self.checkboxes[d] = cbself.drives_layout.addWidget(cb, i // 6, i % 6)def start_indexing(self):roots = [d for d, cb in self.checkboxes.items() if cb.isChecked()]if not roots:QMessageBox.warning(self, "提示", "请至少选择一个盘符!")returnself.btn_update.setEnabled(False)self.btn_stop.setEnabled(True)self.progress_bar.show()self.list_result.clear()self.list_result.addItem("正在建立索引,建立过程中也可以尝试搜索...")self.indexer = FileIndexer()self.thread = threading.Thread(target=self.indexer.build_index, args=(roots,), daemon=True)self.indexer.signals.count_update.connect(lambda n: self.lbl_count.setText(f"已收录: {n:,}"))self.indexer.signals.status.connect(self.lbl_status.setText)self.indexer.signals.current_file.connect(self.lbl_current.setText)self.indexer.signals.finished.connect(self.on_finished)self.thread.start()def stop_indexing(self):self.indexer.stop()self.btn_stop.setEnabled(False)self.lbl_status.setText("正在停止...")def on_finished(self, total):self.progress_bar.hide()self.btn_update.setEnabled(True)self.btn_stop.setEnabled(False)self.lbl_status.setText("索引完成")self.lbl_current.setText("")QMessageBox.information(self, "完成", f"索引更新完毕!\n本次共收录 {total:,} 个项目。")self.list_result.clear()def on_search(self, text):text = text.strip()if not self.db_path.exists():return# 检查过滤条件show_files = self.chk_search_file.isChecked()show_dirs = self.chk_search_dir.isChecked()if not text:self.list_result.clear()returnif not show_files and not show_dirs:self.list_result.clear()self.list_result.addItem("请至少勾选一种类型(文件或文件夹)")returntokens = text.split()fts_query_parts = []for t in tokens:# 加上 * 使得搜索 "con" 能匹配 "config"# 语法:name : "keyword*"fts_query_parts.append(f'name : "{t}*"')fts_query = " AND ".join(fts_query_parts)# 构建类型过滤 SQLtype_filters = []if show_files: type_filters.append(0)if show_dirs: type_filters.append(1)type_sql = ",".join(map(str, type_filters))try:conn = sqlite3.connect(str(self.db_path))cur = conn.cursor()# 核心查询语句sql = f"""SELECT name, path, is_dir FROM files WHERE files MATCH ? AND is_dir IN ({type_sql})ORDER BY rank LIMIT 500"""cur.execute(sql, (fts_query,))rows = cur.fetchall()conn.close()self.list_result.clear()if not rows:self.list_result.addItem("未找到匹配项")returnself.list_result.addItem(f"--- 找到 {len(rows)} 个结果 ---")for name, path, is_dir in rows:icon = "📁" if is_dir else "📄"item_text = f"{icon} {name} → {path}"self.list_result.addItem(item_text)except Exception as e:# 输入特殊字符时可能导致 FTS 语法错误,忽略即可passdef open_item(self, item):text = item.text()if "→" in text:path = text.split("→", 1)[1].strip()try:os.startfile(path)except Exception as e:QMessageBox.warning(self, "错误", f"无法打开路径:\n{path}\n错误信息:{e}")if __name__ == "__main__":app = QApplication(sys.argv)win = MainWindow()win.show()sys.exit(app.exec_())
有什么改进思路希望多多指出