description: 触发性ui交互、
===挂屏小窗脚本===
1.shift+shift键显示/隐藏
2.shift+enter退出程序
3.5分钟更新记录 并触发<后置清晰脚本>
4.ctrl+左右键 可以切换前后记录
===规则===
当处在非最新信息的时候,输入新增文字,此时过去文字+新增文字 组成的新段落 后续会视为最新的一版存入记录.txt!!!
===配套清洗脚本===
1.针对txt中的数据记录 去重相似度超过90%的数据
2.覆盖回原文件
python
import sys
import json
import time
import subprocess
from datetime import datetime
from pathlib import Path
from threading import Thread, Event
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import keyboard
from flask import Flask, request, jsonify
# ====================== 配置 ======================
SCRIPT_DIR = Path(sys.argv[0]).parent
SAVE_FILE = SCRIPT_DIR / "记录.txt"
INTERVAL_MIN = 5
PORT = 16888
HOTKEY = "shift+shift"
CLEAN_SCRIPT = SCRIPT_DIR / "后置清晰.py"
# ====================== 全局变量 ======================
last_saved_content = None
records = []
current_index = -1
last_refresh_time = 0
REFRESH_COOLDOWN = 15
is_editing_based_on_history = False
# ====================== 悬浮窗主窗口 ======================
class FloatWindow(QMainWindow):
updateTextSignal = pyqtSignal(str)
updateLabelSignal = pyqtSignal(str)
def __init__(self):
super().__init__()
self.setWindowTitle("监控悬浮窗")
self.setFixedSize(800, 560)
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool)
self.setAttribute(Qt.WA_TranslucentBackground)
self.central_widget = QWidget()
self.setCentralWidget(self.central_widget)
self.main_layout = QVBoxLayout(self.central_widget)
self.main_layout.setContentsMargins(0,0,0,0)
self.main_layout.setSpacing(0)
self.title_bar = QWidget()
self.title_bar.setFixedHeight(40)
self.title_bar.setCursor(Qt.SizeAllCursor)
self.main_layout.addWidget(self.title_bar)
self.content_widget = QWidget()
self.content_widget.setStyleSheet("background-color:#2c2c2c;border-radius:10px;")
self.content_layout = QVBoxLayout(self.content_widget)
self.main_layout.addWidget(self.content_widget)
self.text_edit = QTextEdit()
self.text_edit.setPlaceholderText("这里是要被抓取的内容")
font = self.text_edit.font()
new_size = int(font.pointSize() * 1.3)
font.setPointSize(new_size)
self.text_edit.setFont(font)
self.page_label = QLabel("📄 实时内容")
self.page_label.setStyleSheet("color:#aaa;padding:5px;")
self.content_layout.addWidget(self.page_label)
self.content_layout.addWidget(self.text_edit)
self.text_edit.setStyleSheet("""
QTextEdit {color:white;background-color:#3a3c3c;border-radius:5px;padding:5px;}
""")
self.dragging = False
self.offset = QPoint()
self.text_edit.textChanged.connect(on_text_edited)
self.updateTextSignal.connect(self.safe_set_text)
# Ctrl+S 快捷键
self.save_shortcut = QShortcut(QKeySequence("Ctrl+S"), self)
self.save_shortcut.activated.connect(self.manual_save)
# ========== 核心:强制保存(一定写入) ==========
def manual_save(self):
try:
content = self.get_content()
if not content:
print("❌ 内容为空,不保存")
return
# 强制保存,无视重复,直接追加写入
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
data = {
"timestamp": int(time.time()),
"time_str": now,
"content": content
}
with open(SAVE_FILE, "a", encoding="utf-8") as f:
f.write(json.dumps(data, ensure_ascii=False) + "\n")
global last_saved_content
last_saved_content = content
load_all_records()
print("✅ Ctrl+S 已强制保存新记录!")
except Exception as e:
print("保存失败:", e)
# ========== 【修复】全窗口可拖动 ==========
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.dragging = True
self.offset = event.globalPos() - self.pos()
event.accept()
def mouseMoveEvent(self, event):
if self.dragging and event.buttons() == Qt.LeftButton:
self.move(event.globalPos() - self.offset)
event.accept()
def mouseReleaseEvent(self, event):
self.dragging = False
event.accept()
def safe_set_text(self, text):
self.text_edit.blockSignals(True)
self.text_edit.setPlainText(text)
self.text_edit.blockSignals(False)
def get_content(self):
return self.text_edit.toPlainText().strip()
# ====================== 辅助函数 ======================
def on_text_edited():
global is_editing_based_on_history, current_index
if len(records) == 0:
return
if current_index != len(records) - 1:
is_editing_based_on_history = True
def load_all_records():
global records, current_index
if not SAVE_FILE.exists():
records = []
current_index = -1
return []
try:
with open(SAVE_FILE, "r", encoding="utf-8") as f:
lines = [line.strip() for line in f if line.strip()]
temp = []
for line in lines:
try:
temp.append(json.loads(line))
except:
continue
temp.sort(key=lambda x: x.get("timestamp", 0))
records = temp
current_index = len(records) - 1
except:
records = []
current_index = -1
def show_record(index):
if not records:
window.updateTextSignal.emit("暂无记录")
return
if 0 <= index < len(records):
txt = records[index]["content"]
window.updateTextSignal.emit(txt)
global current_index
current_index = index
def safe_refresh_records():
global last_refresh_time
if is_editing_based_on_history:
return
now = time.time()
if now - last_refresh_time >= REFRESH_COOLDOWN:
load_all_records()
last_refresh_time = now
# ====================== 方向键 ======================
def arrow_key_listener():
while True:
try:
if keyboard.is_pressed("ctrl+left"):
safe_refresh_records()
if current_index > 0:
show_record(current_index - 1)
time.sleep(0.3)
if keyboard.is_pressed("ctrl+right"):
safe_refresh_records()
if current_index < len(records) - 1:
show_record(current_index + 1)
else:
show_record(len(records)-1)
time.sleep(0.3)
except:
pass
time.sleep(0.05)
# ====================== 保存功能 ======================
def run_clean_script():
if not CLEAN_SCRIPT.exists():
return
try:
si = subprocess.STARTUPINFO()
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
subprocess.Popen([sys.executable, str(CLEAN_SCRIPT)], cwd=str(SCRIPT_DIR),
startupinfo=si, creationflags=subprocess.CREATE_NO_WINDOW)
except:
return
def save_content(content, force=False):
global last_saved_content, is_editing_based_on_history
if not content:
return
if not force and (content == last_saved_content):
return
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
data = {"timestamp": int(time.time()), "time_str": now, "content": content}
try:
with open(SAVE_FILE, "a", encoding="utf-8") as f:
f.write(json.dumps(data, ensure_ascii=False) + "\n")
last_saved_content = content
is_editing_based_on_history = False
load_all_records()
run_clean_script()
except:
return
# ====================== 快捷键 ======================
def timer_task(stop_event):
while not stop_event.is_set():
time.sleep(INTERVAL_MIN * 60)
save_content(window.get_content())
def hotkey_listener():
while True:
keyboard.wait(HOTKEY)
window.setVisible(not window.isVisible())
time.sleep(0.3)
def exit_hotkey_listener(stop_event, app):
while True:
keyboard.wait("shift+enter")
try:
current_content = window.get_content()
save_content(current_content, force=True)
time.sleep(0.1)
except:
pass
stop_event.set()
app.quit()
sys.exit(0)
# ====================== API ======================
app = Flask(__name__)
window = None
@app.route("/status")
def status():
return jsonify({"visible": window.isVisible(), "last": window.get_content()})
@app.route("/capture", methods=["POST"])
def capture():
save_content(window.get_content())
return jsonify({"code":0})
@app.route("/toggle", methods=["POST"])
def toggle():
window.setVisible(not window.isVisible())
return jsonify({"code":0})
def run_api():
app.run(host="0.0.0.0", port=PORT, debug=False, use_reloader=False)
# ====================== 主程序 ======================
def main():
global window
app_qt = QApplication(sys.argv)
window = FloatWindow()
window.show()
load_all_records()
try:
with open(SAVE_FILE, "r", encoding="utf-8") as f:
lines = [l.strip() for l in f if l.strip()]
if lines:
last = json.loads(lines[-1])["content"]
window.updateTextSignal.emit(last)
global last_saved_content
last_saved_content = last
except:
pass
Thread(target=hotkey_listener, daemon=True).start()
Thread(target=arrow_key_listener, daemon=True).start()
stop_event = Event()
Thread(target=timer_task, args=(stop_event,), daemon=True).start()
Thread(target=run_api, daemon=True).start()
Thread(target=exit_hotkey_listener, args=(stop_event, app_qt), daemon=True).start()
app_qt.exec_()
stop_event.set()
if __name__ == "__main__":
main()
part2:======================
python
import json
from difflib import SequenceMatcher
SIMILARITY_THRESHOLD = 0.9
def similar(a, b):
return SequenceMatcher(None, a, b).ratio()
with open("记录.txt", "r", encoding="utf-8") as f:
lines = [line.rstrip("\n") for line in f if line.strip()]
items = []
for line in lines:
try:
d = json.loads(line)
items.append((line, d.get("content", "")))
except:
items.append((line, ""))
unique = []
contents = []
for raw, cnt in items:
dup = False
for c in contents:
if similar(cnt, c) >= SIMILARITY_THRESHOLD:
dup = True
break
if not dup:
unique.append(raw)
contents.append(cnt)
with open("记录.txt", "w", encoding="utf-8") as f:
f.write("\n".join(unique) + "\n")
print(f"去重完成!原条目:{len(items)},保留:{len(unique)},已覆盖原文件")