import time
import tkinter as tk
from tkinter import messagebox, ttk, filedialog
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
import threading
import subprocess
import os
import tempfile
import atexit
import json
from datetime import datetime
import sqlite3
from list import TestCaseTableWidget
import pandas as pd
import chardet
import os
class ListThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.daemon = True # 设置为守护线程,当主程序退出时,子线程也会退出
def run(self):
# 在新线程中启动PyQt5应用
import sys
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
window = TestCaseTableWidget()
window.show()
sys.exit(app.exec_())
class WebFormFiller:
def __init__(self):
self.driver = None
self.chrome_process = None
self.user_data_dir = None
self.root = tk.Tk()
self.root.title("网页表单自动填充工具")
self.root.geometry("1200x800")
self.root.resizable(True, True)
# 数据库文件路径
self.db_file_path = "test_cases.db"
# 初始化数据库
self.init_database()
# 注册退出清理函数
# atexit.register(self.cleanup)
# 创建主界面
self.create_widgets()
def init_database(self):
"""初始化SQLite数据库"""
try:
conn = sqlite3.connect(self.db_file_path)
cursor = conn.cursor()
# 创建富文本数据表
cursor.execute('''
CREATE TABLE IF NOT EXISTS test_cases (
id INTEGER PRIMARY KEY AUTOINCREMENT,
case_id TEXT UNIQUE,
html_content TEXT,
plain_text TEXT,
created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
conn.commit()
conn.close()
print("数据库初始化成功")
except Exception as e:
print(f"数据库初始化失败: {e}")
def create_widgets(self):
# 创建主框架
main_frame = tk.Frame(self.root)
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 标题
title_label = tk.Label(main_frame, text="网页表单自动填充工具",
font=("Arial", 18, "bold"))
title_label.pack(pady=10)
# 创建主水平布局容器
main_container = tk.Frame(main_frame)
main_container.pack(fill=tk.BOTH, expand=True)
# 左侧区域 - 表单数据和配置
left_frame = tk.Frame(main_container)
left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5)
# 右侧区域 - 日志和浏览器设置
right_frame = tk.Frame(main_container)
right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5)
# ========== 左侧区域内容 ==========
# URL输入区域
url_frame = tk.LabelFrame(left_frame, text="网页设置", padx=10, pady=10)
url_frame.pack(fill=tk.X, pady=5)
tk.Label(url_frame, text="目标网页URL:", font=("Arial", 12, "bold")).pack(anchor="w")
self.url_entry = tk.Entry(url_frame, width=60)
self.url_entry.pack(fill=tk.X, pady=5)
self.url_entry.insert(0, "https://jira-phone.mioffice.cn")
# 添加文件选择框架(垂直布局)
file_frame = tk.LabelFrame(left_frame, text="文件选择", padx=10, pady=10)
file_frame.pack(fill=tk.X, pady=10)
# 第一个文件选择框(旧文件)
file1_frame = tk.Frame(file_frame)
file1_frame.pack(fill=tk.X, pady=2)
tk.Label(file1_frame, text="旧版本CSV文件:", font=("Arial", 10)).pack(side=tk.LEFT, padx=(0, 5))
self.old_file_entry = tk.Entry(file1_frame, width=40)
self.old_file_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))
tk.Button(file1_frame, text="浏览", command=self.browse_old_file).pack(side=tk.LEFT)
file2_frame = tk.Frame(file_frame)
file2_frame.pack(fill=tk.X, pady=5)
tk.Label(file2_frame, text="新版本CSV文件:", font=("Arial", 10)).pack(side=tk.LEFT, padx=(0, 5))
self.new_file_entry = tk.Entry(file2_frame, width=40)
self.new_file_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))
tk.Button(file2_frame, text="浏览", command=self.browse_new_file).pack(side=tk.LEFT)
# 表单数据区域 - 水平布局
tk.Button(file2_frame, text="检索", command=self.start_compare_csv).pack(side=tk.LEFT)
# 表单数据区域 - 水平布局
data_frame = tk.LabelFrame(left_frame, text="表单数据", padx=10, pady=10)
data_frame.pack(fill=tk.BOTH, expand=True, pady=5)
# # 序列号选择区域
serial_frame = tk.Frame(data_frame)
serial_frame.pack(fill=tk.X, pady=5)
# self.serial_combo = ttk.Combobox(serial_frame, width=30, state="normal")
# self.serial_combo.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
self.filtered_items = [] # 当前显示的过滤后选项
# 创建Combobox
tk.Label(serial_frame, text="选择序列号:").pack(side=tk.LEFT, padx=5)
self.serial_combo = ttk.Combobox(serial_frame,width=30,values=self.filtered_items,state="normal")
self.serial_combo.pack(side=tk.LEFT, fill=tk.X, expand=True)
# 绑定键盘事件
self.serial_combo.bind("<KeyRelease>", self.on_key_release)
# 绑定选择事件
self.serial_combo.bind("<<ComboboxSelected>>", self.on_serial_changed)
# 添加清除按钮
self.clear_btn = ttk.Button(serial_frame, text="×", width=2, command=self.clear_input)
self.clear_btn.pack(side=tk.RIGHT, padx=(2, 0))
refresh_button = tk.Button(serial_frame, text="刷新", command=self.refresh_serial_list,
bg="#2196F3", fg="white", font=("Arial", 8))
refresh_button.pack(side=tk.LEFT, padx=5)
# 绑定下拉框选择事件
# self.serial_combo.bind('<<ComboboxSelected>>', self.on_serial_changed)
# 文本插入区域 - 水平布局
text_inputs_frame = tk.Frame(data_frame)
text_inputs_frame.pack(fill=tk.BOTH, expand=True, pady=5)
# 文本插入1
text1_frame = tk.LabelFrame(text_inputs_frame, text="文本插入1", padx=5, pady=5)
text1_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5)
self.text_input1 = tk.Text(text1_frame, width=40, height=8, font=("Arial", 10))
text1_scrollbar = ttk.Scrollbar(text1_frame, orient="vertical", command=self.text_input1.yview)
self.text_input1.configure(yscrollcommand=text1_scrollbar.set)
self.text_input1.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
text1_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.text_input1.insert("1.0", "这是第一个文本插入区域的内容。")
# 文本插入2
text2_frame = tk.LabelFrame(text_inputs_frame, text="文本插入2", padx=5, pady=5)
text2_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5)
self.text_input2 = tk.Text(text2_frame, width=40, height=8, font=("Arial", 10))
text2_scrollbar = ttk.Scrollbar(text2_frame, orient="vertical", command=self.text_input2.yview)
self.text_input2.configure(yscrollcommand=text2_scrollbar.set)
self.text_input2.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
text2_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.text_input2.insert("1.0", "这是第二个文本插入区域的内容。")
# 配置区域 - 水平布局
config_frame = tk.Frame(left_frame)
config_frame.pack(fill=tk.X, pady=5)
# 字段映射配置
mapping_frame = tk.LabelFrame(config_frame, text="字段映射配置", padx=10, pady=10)
mapping_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5)
tk.Label(mapping_frame, text="字段映射:", font=("Arial", 9, "bold")).pack(anchor="w")
# 创建字段映射表格
fields = [
("字段1标识", "text_input1"),
("字段2标识", "text_input2")
]
self.field_mappings = {}
for i, (field_name, field_key) in enumerate(fields):
row_frame = tk.Frame(mapping_frame)
row_frame.pack(fill=tk.X, pady=2)
tk.Label(row_frame, text=f"{field_name}:", width=12, anchor="w").pack(side=tk.LEFT)
entry = tk.Entry(row_frame, width=20)
entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
# 设置默认的映射值
if field_key == "text_input1":
entry.insert(0, "测试用例id")
else:
entry.insert(0, "描述")
self.field_mappings[field_key] = entry
# 富文本编辑器配置
rich_text_frame = tk.LabelFrame(config_frame, text="富文本编辑器设置", padx=10, pady=10)
rich_text_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5)
# TinyMCE配置
tinymce_frame = tk.Frame(rich_text_frame)
tinymce_frame.pack(fill=tk.X, pady=5)
self.auto_detect_tinymce = tk.BooleanVar(value=True)
tk.Checkbutton(tinymce_frame, text="自动识别TinyMCE编辑器",
variable=self.auto_detect_tinymce, font=("Arial", 9)).pack(anchor="w")
self.tinymce_content_type = tk.StringVar(value="text")
content_type_frame = tk.Frame(rich_text_frame)
content_type_frame.pack(fill=tk.X, pady=5)
tk.Label(content_type_frame, text="内容类型:", font=("Arial", 9)).pack(side=tk.LEFT)
tk.Radiobutton(content_type_frame, text="纯文本", variable=self.tinymce_content_type,
value="text", font=("Arial", 8)).pack(side=tk.LEFT, padx=5)
tk.Radiobutton(content_type_frame, text="HTML格式", variable=self.tinymce_content_type,
value="html", font=("Arial", 8)).pack(side=tk.LEFT, padx=5)
# 自定义字符检测区域
char_detection_frame = tk.LabelFrame(config_frame, text="自定义字符检测", padx=10, pady=10)
char_detection_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5)
# 自定义字符输入
char_input_frame = tk.Frame(char_detection_frame)
char_input_frame.pack(fill=tk.X, pady=5)
tk.Label(char_input_frame, text="检测字符:").pack(anchor="w")
self.char_entry = tk.Entry(char_input_frame, width=30)
self.char_entry.pack(fill=tk.X, pady=2)
self.char_entry.insert(0, "请填写检测字符") # 默认检测字符
# 检测间隔设置
interval_frame = tk.Frame(char_detection_frame)
interval_frame.pack(fill=tk.X, pady=5)
tk.Label(interval_frame, text="检测间隔(秒):").pack(side=tk.LEFT)
self.interval_entry = tk.Entry(interval_frame, width=10)
self.interval_entry.pack(side=tk.LEFT, padx=5)
self.interval_entry.insert(0, "60")
# 检测控制按钮
button_frame = tk.Frame(char_detection_frame)
button_frame.pack(fill=tk.X, pady=5)
self.start_detect_button = tk.Button(button_frame, text="开始检测", command=self.start_char_detection,
bg="#2196F3", fg="white", font=("Arial", 9))
self.start_detect_button.pack(side=tk.LEFT, padx=2)
self.stop_detect_button = tk.Button(button_frame, text="停止检测", command=self.stop_char_detection,
bg="#f44336", fg="white", font=("Arial", 9))
self.stop_detect_button.pack(side=tk.LEFT, padx=2)
# ========== 右侧区域内容 ==========
# 检测日志区域
log_frame = tk.LabelFrame(right_frame, text="检测日志", padx=10, pady=10)
log_frame.pack(fill=tk.BOTH, expand=True, pady=5)
# 创建日志文本框和滚动条
self.log_text = tk.Text(log_frame, width=60, height=15, font=("Consolas", 9))
log_scrollbar = ttk.Scrollbar(log_frame, orient="vertical", command=self.log_text.yview)
self.log_text.configure(yscrollcommand=log_scrollbar.set)
self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
log_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# 清空日志按钮
clear_log_button = tk.Button(log_frame, text="清空日志", command=self.clear_log,
bg="#FF9800", fg="white", font=("Arial", 9))
clear_log_button.pack(side=tk.BOTTOM, pady=5)
# 调试模式选项区域
debug_frame = tk.LabelFrame(right_frame, text="浏览器设置", padx=10, pady=10)
debug_frame.pack(fill=tk.X, pady=5)
# 调试端口设置
port_frame = tk.Frame(debug_frame)
port_frame.pack(fill=tk.X, pady=5)
tk.Label(port_frame, text="调试端口:").pack(side=tk.LEFT)
self.port_entry = tk.Entry(port_frame, width=10)
self.port_entry.pack(side=tk.LEFT, padx=5)
self.port_entry.insert(0, "9222")
# 自动填写选项
auto_options_frame = tk.Frame(debug_frame)
auto_options_frame.pack(fill=tk.X, pady=5)
self.auto_detect_var = tk.BooleanVar(value=True)
tk.Checkbutton(auto_options_frame, text="自动探测表单字段",
variable=self.auto_detect_var, font=("Arial", 9)).pack(side=tk.LEFT, padx=10)
self.delay_fill_var = tk.BooleanVar(value=True)
tk.Checkbutton(auto_options_frame, text="延迟填充(推荐)",
variable=self.delay_fill_var, font=("Arial", 9)).pack(side=tk.LEFT, padx=10)
# ========== 底部按钮区域 ==========
# 按钮区域
button_frame = tk.Frame(main_frame)
button_frame.pack(pady=20)
# 第一行按钮
button_row1 = tk.Frame(button_frame)
button_row1.pack(pady=5)
self.start_button = tk.Button(button_row1, text="开始自动填充",
command=self.start_filling,
bg="#4CAF50", fg="white",
font=("Arial", 12, "bold"),
width=15, height=2)
self.start_button.pack(side=tk.LEFT, padx=15)
self.stop_button = tk.Button(button_row1, text="停止",
command=self.stop_filling,
bg="#f44336", fg="white",
font=("Arial", 12, "bold"),
width=15, height=2)
self.stop_button.pack(side=tk.LEFT, padx=15)
# 第二行按钮
button_row2 = tk.Frame(button_frame)
button_row2.pack(pady=5)
self.launch_browser_button = tk.Button(button_row2, text="启动浏览器",
command=self.launch_browser,
bg="#FF9800", fg="white",
font=("Arial", 10, "bold"),
width=10, height=1)
self.launch_browser_button.pack(side=tk.LEFT, padx=5)
self.test_button = tk.Button(button_row2, text="连接浏览器",
command=self.connect_browser,
bg="#2196F3", fg="white",
font=("Arial", 10),
width=10, height=1)
self.test_button.pack(side=tk.LEFT, padx=5)
self.test_button = tk.Button(button_row2, text="列表文件",
command=self.DK,
bg="#2196F3", fg="white",
font=("Arial", 10),
width=10, height=1)
self.test_button.pack(side=tk.LEFT, padx=5)
self.test_button = tk.Button(button_row2, text="探测表单",
command=self.detect_form_fields,
bg="#2196F3", fg="white",
font=("Arial", 10),
width=10, height=1)
self.test_button.pack(side=tk.LEFT, padx=5)
# 状态显示
self.status_var = tk.StringVar()
self.status_var.set("准备就绪")
status_label = tk.Label(main_frame, textvariable=self.status_var,
relief=tk.SUNKEN, anchor=tk.W, bg="#e0e0e0",
font=("Arial", 10))
status_label.pack(fill=tk.X, pady=5)
# 进度条
self.progress = ttk.Progressbar(main_frame, mode='indeterminate')
self.progress.pack(fill=tk.X, pady=5)
# 字符检测控制变量
self.char_detection_running = False
self.char_detection_timer = None
# 初始化序列号列表
self.refresh_serial_list()
self.all_case_ids = [] # 存储所有Case ID用于搜索
def on_key_release(self, event):
"""处理键盘释放事件,动态过滤选项"""
# 获取当前输入框内容
current_text = self.serial_combo.get()
# 过滤选项:只保留包含当前输入文本的选项
self.filtered_items = [
item for item in self.all_case_ids
if current_text.lower() in item.lower()
]
# 更新下拉列表
self.serial_combo["values"] = self.filtered_items
# 如果过滤后还有选项,自动打开下拉列表
if self.filtered_items and event.keysym not in ["Return", "Escape"]:
self.serial_combo.event_generate('<Down>')
def on_select(self, event):
"""当选择下拉项后,更新输入框为完整内容"""
selected = self.serial_combo.get()
self.serial_combo.delete(0, tk.END)
self.serial_combo.insert(0, selected)
def clear_input(self):
"""清除输入框内容并重置下拉列表"""
self.serial_combo.set("")
self.serial_combo["values"] = self.all_case_ids
self.filtered_items = self.all_case_ids
def get(self):
"""获取当前选择的值"""
return self.serial_combo.get()
def set(self, value):
"""设置当前值"""
self.serial_combo.set(value)
serial_combo 控件输入要等文字显示在输入框中,才实现检索功能,下拉框显示搜索的数据
最新发布