1
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/.idea/*
|
||||||
|
/data.db
|
||||||
|
/.old/*
|
||||||
195
backend.py
Normal file
195
backend.py
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
import os
|
||||||
|
import random
|
||||||
|
import sqlite3
|
||||||
|
import time
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
|
||||||
|
class Backend:
|
||||||
|
|
||||||
|
def __init__(self, db="data.db"):
|
||||||
|
self.db_path = db
|
||||||
|
# 先解压
|
||||||
|
if not os.path.exists(self.db_path) and os.path.exists(self.db_path + ".zip"):
|
||||||
|
with zipfile.ZipFile(self.db_path + ".zip", 'r') as zip_ref:
|
||||||
|
zip_ref.extractall(".")
|
||||||
|
|
||||||
|
|
||||||
|
self.time = time.time()
|
||||||
|
self.title_filter = self.get_source_type()
|
||||||
|
self.global_filter = []
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_question(self):
|
||||||
|
conn = sqlite3.connect(self.db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# 构造 IN 子句的占位符
|
||||||
|
placeholders = ','.join('?' for _ in self.title_filter)
|
||||||
|
|
||||||
|
# 总权重查询
|
||||||
|
total_query = f"""
|
||||||
|
SELECT SUM(count)
|
||||||
|
FROM questions
|
||||||
|
WHERE count > 0 AND title IN ({placeholders})
|
||||||
|
"""
|
||||||
|
|
||||||
|
#print(total_query)
|
||||||
|
#print(self.title_filter)
|
||||||
|
|
||||||
|
cursor.execute(total_query, self.title_filter)
|
||||||
|
total_weights = cursor.fetchone()[0]
|
||||||
|
if total_weights is None or total_weights == 0:
|
||||||
|
conn.close()
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 生成随机数 [0, total_weights)
|
||||||
|
random_num = random.uniform(0, total_weights)
|
||||||
|
|
||||||
|
# 主查询:加权随机选择
|
||||||
|
query = f"""
|
||||||
|
SELECT *
|
||||||
|
FROM (
|
||||||
|
SELECT *,
|
||||||
|
SUM(count) OVER (ORDER BY id ROWS UNBOUNDED PRECEDING) AS cum_weight
|
||||||
|
FROM questions
|
||||||
|
WHERE count > 0 AND title IN ({placeholders})
|
||||||
|
)
|
||||||
|
WHERE ? < cum_weight
|
||||||
|
ORDER BY cum_weight
|
||||||
|
LIMIT 1;
|
||||||
|
"""
|
||||||
|
|
||||||
|
params = tuple(self.title_filter) + (random_num,)
|
||||||
|
cursor.execute(query, params)
|
||||||
|
|
||||||
|
row = cursor.fetchone()
|
||||||
|
print(total_weights, row)
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
self.time = time.time()
|
||||||
|
|
||||||
|
return list(row) if row else None
|
||||||
|
|
||||||
|
def update(self, id, state):
|
||||||
|
conn = sqlite3.connect(self.db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# 作对-1 错了+2
|
||||||
|
if state != 0:
|
||||||
|
query = """
|
||||||
|
UPDATE questions
|
||||||
|
SET count = count + 2
|
||||||
|
WHERE id = ?
|
||||||
|
and count > 0"""
|
||||||
|
else:
|
||||||
|
query = """
|
||||||
|
UPDATE questions
|
||||||
|
SET count = count - 1
|
||||||
|
WHERE id = ?
|
||||||
|
"""
|
||||||
|
cursor.execute(query, (id,))
|
||||||
|
|
||||||
|
# 记录用时
|
||||||
|
query = "INSERT INTO answers_history (id, time_used, state) VALUES (?, ?, ?)"
|
||||||
|
cursor.execute(query, (id, time.time() - self.time, state))
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def reset_time(self):
|
||||||
|
self.time = time.time()
|
||||||
|
|
||||||
|
def get_acc(self, top_n=0):
|
||||||
|
conn = sqlite3.connect(self.db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
|
if top_n > 0:
|
||||||
|
query = """
|
||||||
|
SELECT CAST(SUM(CASE WHEN state = 0 THEN 1 ELSE 0 END) AS FLOAT) / COUNT(*) AS accuracy
|
||||||
|
FROM (SELECT state
|
||||||
|
FROM answers_history
|
||||||
|
ORDER BY time DESC
|
||||||
|
LIMIT ?)
|
||||||
|
"""
|
||||||
|
params = (top_n,)
|
||||||
|
else:
|
||||||
|
query = """
|
||||||
|
SELECT CAST(SUM(CASE WHEN state = 0 THEN 1 ELSE 0 END) AS FLOAT) / COUNT(*) AS accuracy FROM answers_history
|
||||||
|
"""
|
||||||
|
params = ()
|
||||||
|
|
||||||
|
cursor.execute(query, params)
|
||||||
|
result = cursor.fetchone()[0]
|
||||||
|
return result * 100 if result is not None else 0.0
|
||||||
|
|
||||||
|
except sqlite3.Error as e:
|
||||||
|
print(f"数据库错误:{e}")
|
||||||
|
return 0.0
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def get_avg_time(self, top_n=0):
|
||||||
|
conn = sqlite3.connect(self.db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
|
if top_n > 0:
|
||||||
|
query = """
|
||||||
|
SELECT CAST(SUM(time_used) AS FLOAT) / COUNT(*) AS accuracy
|
||||||
|
FROM (SELECT time_used
|
||||||
|
FROM answers_history
|
||||||
|
where time_used is not null
|
||||||
|
ORDER BY time DESC
|
||||||
|
LIMIT ?)
|
||||||
|
"""
|
||||||
|
params = (top_n,)
|
||||||
|
else:
|
||||||
|
query = """
|
||||||
|
SELECT CAST(SUM(time_used) AS FLOAT) / COUNT(*) AS accuracy FROM answers_history where time_used is not null
|
||||||
|
"""
|
||||||
|
params = ()
|
||||||
|
|
||||||
|
cursor.execute(query, params)
|
||||||
|
result = cursor.fetchone()[0]
|
||||||
|
return result if result is not None else 0.0
|
||||||
|
|
||||||
|
except sqlite3.Error as e:
|
||||||
|
print(f"数据库错误:{e}")
|
||||||
|
return 0.0
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def get_statistics(self):
|
||||||
|
avg_all = self.get_avg_time()
|
||||||
|
avg_50 = self.get_avg_time(50)
|
||||||
|
avg_100 = self.get_avg_time(100)
|
||||||
|
|
||||||
|
result = [
|
||||||
|
["正确率", f"{self.get_acc():.1f}/60%"],
|
||||||
|
[],
|
||||||
|
["最近50题正确率", f"{self.get_acc(50):.1f}/60%"],
|
||||||
|
["最近100题正确率", f"{self.get_acc(120):.1f}/60%"],
|
||||||
|
["平均耗时", f"{avg_all:.1f}/72s"],
|
||||||
|
["预计做完用时", f"{avg_all * 1.667:.1f}/120min"],
|
||||||
|
["50平均耗时", f"{avg_50:.1f}/72s"],
|
||||||
|
["预计做完用时", f"{avg_50 * 1.667:.1f}/120min"],
|
||||||
|
["120平均耗时", f"{avg_100:.1f}/72s"],
|
||||||
|
["预计做完用时", f"{avg_100 * 1.667:.1f}/120min"],
|
||||||
|
]
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_source_type(self):
|
||||||
|
conn = sqlite3.connect(self.db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
query = "SELECT DISTINCT title FROM questions"
|
||||||
|
cursor.execute(query)
|
||||||
|
result = cursor.fetchall()
|
||||||
|
new_list = [item[0] for item in result]
|
||||||
|
return new_list
|
||||||
|
|
||||||
|
def set_config(self,a,b):
|
||||||
|
self.title_filter = a
|
||||||
|
self.global_filter = b
|
||||||
4
components/__init__.py
Normal file
4
components/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
from .constants import *
|
||||||
|
from .page0 import Page0
|
||||||
|
from .page1 import Page1
|
||||||
|
from .page2 import Page2
|
||||||
26
components/constants.py
Normal file
26
components/constants.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import tkinter.font as font
|
||||||
|
|
||||||
|
# 定义
|
||||||
|
BG_COLOR = "#f4f6f9" # 背景色
|
||||||
|
FG_COLOR = "#2c3e50" # 主文字色
|
||||||
|
ACCENT_COLOR = "#3498db" # 主按钮色
|
||||||
|
CORRECT_COLOR = "#d4edda" # 正确答案 淡绿色
|
||||||
|
MISS_COLOR = "#5bc04b" # 漏选 绿色
|
||||||
|
WRONG_COLOR = "#f8d7da" # 错误 红色
|
||||||
|
SELECTED_COLOR = "#d6eaf8" # 选中/悬浮 浅蓝色
|
||||||
|
FONT_FAMILY = "微软雅黑"
|
||||||
|
|
||||||
|
# 映射
|
||||||
|
indexMap = {6: "A. ", 7: "B. ", 8: "C. ", 9: "D. "}
|
||||||
|
|
||||||
|
# 全局
|
||||||
|
bottom_options = ["全部的题", "未做过的题", "错过的题", ]
|
||||||
|
instructions = f"""后面的选项仅在'{bottom_options[0]}'未被勾选时起效"""
|
||||||
|
|
||||||
|
# 样式
|
||||||
|
# 创建字体
|
||||||
|
class Fonts:
|
||||||
|
def __init__(self):
|
||||||
|
self.title = font.Font(family=FONT_FAMILY, size=18, weight="bold")
|
||||||
|
self.button = font.Font(family=FONT_FAMILY, size=14)
|
||||||
|
self.text = font.Font(family=FONT_FAMILY, size=13)
|
||||||
307
components/page0.py
Normal file
307
components/page0.py
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
# 答题页
|
||||||
|
|
||||||
|
import tkinter as tk
|
||||||
|
import random
|
||||||
|
from functools import partial
|
||||||
|
from components import *
|
||||||
|
|
||||||
|
# 选项
|
||||||
|
class WrappingButton(tk.Frame):
|
||||||
|
def __init__(self, parent, text="", command=None,title_font=None,button_font=None,text_font=None, **kwargs):
|
||||||
|
tk.Frame.__init__(self, parent, **kwargs)
|
||||||
|
self.configure(bg=BG_COLOR, relief="raised", bd=1)
|
||||||
|
|
||||||
|
self.title_front = title_font
|
||||||
|
self.button_font = button_font
|
||||||
|
self.text_font = text_font
|
||||||
|
|
||||||
|
self.label = tk.Label(
|
||||||
|
self,
|
||||||
|
text=text,
|
||||||
|
justify="left",
|
||||||
|
anchor="w",
|
||||||
|
wraplength=800,
|
||||||
|
padx=15,
|
||||||
|
pady=12,
|
||||||
|
bg=BG_COLOR,
|
||||||
|
fg=FG_COLOR,
|
||||||
|
font=self.button_font
|
||||||
|
)
|
||||||
|
self.label.pack(fill="both", expand=True)
|
||||||
|
|
||||||
|
# 绑定点击事件
|
||||||
|
self.bind("<Button-1>", lambda e: command() if command else None)
|
||||||
|
self.label.bind("<Button-1>", lambda e: command() if command else None)
|
||||||
|
|
||||||
|
# 绑定鼠标悬停事件
|
||||||
|
self.bind("<Enter>", self._on_enter)
|
||||||
|
self.bind("<Leave>", self._on_leave)
|
||||||
|
self.label.bind("<Enter>", self._on_enter)
|
||||||
|
self.label.bind("<Leave>", self._on_leave)
|
||||||
|
|
||||||
|
# 当前状态
|
||||||
|
self.current_style = "Default"
|
||||||
|
self._state = "normal"
|
||||||
|
|
||||||
|
def _on_enter(self, event):
|
||||||
|
if self._state == "disabled":
|
||||||
|
return
|
||||||
|
if self.current_style == "Default":
|
||||||
|
self.label.configure(bg=SELECTED_COLOR)
|
||||||
|
self.configure(bg=SELECTED_COLOR)
|
||||||
|
|
||||||
|
def _on_leave(self, event):
|
||||||
|
if self._state == "disabled":
|
||||||
|
return
|
||||||
|
if self.current_style == "Default":
|
||||||
|
self.label.configure(bg=BG_COLOR)
|
||||||
|
self.configure(bg=BG_COLOR)
|
||||||
|
|
||||||
|
def config(self, **kwargs):
|
||||||
|
if "text" in kwargs:
|
||||||
|
self.label.config(text=kwargs["text"])
|
||||||
|
kwargs.pop("text")
|
||||||
|
|
||||||
|
if "style" in kwargs:
|
||||||
|
style = kwargs["style"].split(".")[0]
|
||||||
|
self.current_style = style
|
||||||
|
|
||||||
|
if style == "Default":
|
||||||
|
self.label.configure(bg=BG_COLOR, fg=FG_COLOR)
|
||||||
|
self.configure(bg=BG_COLOR)
|
||||||
|
elif style == "Selected":
|
||||||
|
self.label.configure(bg=SELECTED_COLOR, fg=FG_COLOR)
|
||||||
|
self.configure(bg=SELECTED_COLOR)
|
||||||
|
elif style == "Correct":
|
||||||
|
self.label.configure(bg=CORRECT_COLOR, fg="#155724")
|
||||||
|
self.configure(bg=CORRECT_COLOR)
|
||||||
|
elif style == "Miss":
|
||||||
|
self.label.configure(bg=MISS_COLOR, fg="#155724")
|
||||||
|
self.configure(bg=MISS_COLOR)
|
||||||
|
elif style == "Wrong":
|
||||||
|
self.label.configure(bg=WRONG_COLOR, fg="#721c24")
|
||||||
|
self.configure(bg=WRONG_COLOR)
|
||||||
|
|
||||||
|
kwargs.pop("style")
|
||||||
|
|
||||||
|
if "state" in kwargs:
|
||||||
|
self._state = kwargs["state"]
|
||||||
|
if kwargs["state"] == "disabled":
|
||||||
|
self.label.configure(fg="#999999")
|
||||||
|
else:
|
||||||
|
self.label.configure(fg=FG_COLOR)
|
||||||
|
kwargs.pop("state")
|
||||||
|
|
||||||
|
tk.Frame.config(self, **kwargs)
|
||||||
|
|
||||||
|
class Page0(tk.Frame):
|
||||||
|
# 定义
|
||||||
|
indexMap = {6: "A. ", 7: "B. ", 8: "C. ", 9: "D. "} # 映射
|
||||||
|
|
||||||
|
def __init__(self, parent,fonts,c_backend, **kwargs):
|
||||||
|
tk.Frame.__init__(self, parent, **kwargs)
|
||||||
|
# 全局变量
|
||||||
|
self.buttons = []
|
||||||
|
self.text1 = None
|
||||||
|
self.text2 = None
|
||||||
|
self.row = []
|
||||||
|
self.correct_answer = []
|
||||||
|
self.my_answer = [0, 0, 0, 0]
|
||||||
|
self.shuffle_index = [6, 7, 8, 9]
|
||||||
|
self.flag = False
|
||||||
|
self.button_disable = True
|
||||||
|
|
||||||
|
# 入参
|
||||||
|
self.c_backend = c_backend
|
||||||
|
|
||||||
|
# 题目
|
||||||
|
frame_top = tk.Frame(parent, bg=BG_COLOR)
|
||||||
|
frame_top.pack(pady=15, padx=20, fill='x')
|
||||||
|
|
||||||
|
self.text1 = tk.Text(
|
||||||
|
frame_top,
|
||||||
|
font=fonts.title,
|
||||||
|
wrap='word',
|
||||||
|
height=5,
|
||||||
|
bg="white",
|
||||||
|
fg=FG_COLOR,
|
||||||
|
relief="flat",
|
||||||
|
padx=15,
|
||||||
|
pady=15,
|
||||||
|
selectbackground=ACCENT_COLOR,
|
||||||
|
highlightbackground=BG_COLOR
|
||||||
|
)
|
||||||
|
self.text1.config(state="disabled")
|
||||||
|
self.text1.pack(fill='x', expand=True)
|
||||||
|
|
||||||
|
# 按钮
|
||||||
|
frame_buttons = tk.Frame(parent, bg=BG_COLOR)
|
||||||
|
frame_buttons.pack(pady=10, padx=20, fill='x')
|
||||||
|
|
||||||
|
for i in range(4):
|
||||||
|
btn = WrappingButton(
|
||||||
|
frame_buttons,
|
||||||
|
text=f"选项 {i + 1}",
|
||||||
|
command=partial(self.on_button_click, i),
|
||||||
|
title_font=fonts.title,
|
||||||
|
button_font=fonts.button,
|
||||||
|
text_font=fonts.text,
|
||||||
|
)
|
||||||
|
self.buttons.append(btn)
|
||||||
|
btn.pack(pady=6, fill='x')
|
||||||
|
|
||||||
|
# 确定按钮
|
||||||
|
button5 = tk.Button(
|
||||||
|
parent,
|
||||||
|
text="确定",
|
||||||
|
font=fonts.button,
|
||||||
|
bg=ACCENT_COLOR,
|
||||||
|
fg="white",
|
||||||
|
command=self.on_ok_click,
|
||||||
|
relief="flat",
|
||||||
|
padx=20,
|
||||||
|
pady=10,
|
||||||
|
borderwidth=0,
|
||||||
|
cursor="hand2"
|
||||||
|
)
|
||||||
|
button5.pack(pady=12)
|
||||||
|
|
||||||
|
def on_enter(e): button5.config(bg="#2980b9")
|
||||||
|
|
||||||
|
def on_leave(e): button5.config(bg=ACCENT_COLOR)
|
||||||
|
|
||||||
|
button5.bind("<Enter>", on_enter)
|
||||||
|
button5.bind("<Leave>", on_leave)
|
||||||
|
|
||||||
|
# 解析区
|
||||||
|
frame_bottom = tk.Frame(parent, bg=BG_COLOR)
|
||||||
|
frame_bottom.pack(pady=10, padx=20, fill='both', expand=True)
|
||||||
|
|
||||||
|
self.text2 = tk.Text(
|
||||||
|
frame_bottom,
|
||||||
|
font=fonts.text,
|
||||||
|
wrap='word',
|
||||||
|
bg="white",
|
||||||
|
fg="#555",
|
||||||
|
relief="flat",
|
||||||
|
padx=15,
|
||||||
|
pady=15,
|
||||||
|
spacing3=10,
|
||||||
|
selectbackground=ACCENT_COLOR
|
||||||
|
)
|
||||||
|
self.text2.config(state="disabled")
|
||||||
|
|
||||||
|
scroll = tk.Scrollbar(frame_bottom, orient="vertical", command=self.text2.yview)
|
||||||
|
self.text2.configure(yscrollcommand=scroll.set)
|
||||||
|
self.text2.pack(side="left", fill='both', expand=True)
|
||||||
|
scroll.pack(side="right", fill='y')
|
||||||
|
|
||||||
|
def _show_result(self, ):
|
||||||
|
if self.row == None or len(self.row) < 15:
|
||||||
|
return
|
||||||
|
elif self.button_disable:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.button_disable = True
|
||||||
|
|
||||||
|
self.flag = True
|
||||||
|
|
||||||
|
self.text2.config(state="normal")
|
||||||
|
self.text2.delete("1.0", "end")
|
||||||
|
self.text2.insert("end", self.row[14])
|
||||||
|
self.text2.config(state="disabled")
|
||||||
|
|
||||||
|
correct = 0
|
||||||
|
|
||||||
|
for i in range(4):
|
||||||
|
state = self.correct_answer[i] + 2 * self.my_answer[i] # 0:未选对, 1:漏选, 2:错选, 3:选对
|
||||||
|
full_text = self.indexMap.get(self.shuffle_index[i]) + self.row[self.shuffle_index[i]]
|
||||||
|
|
||||||
|
if state == 1: # 漏选(正确但未选)→ 绿色
|
||||||
|
self.buttons[i].config(text=full_text, style="Miss.TButton")
|
||||||
|
elif state == 2: # 选错 → 红色
|
||||||
|
self.buttons[i].config(text=full_text, style="Wrong.TButton")
|
||||||
|
correct = 1
|
||||||
|
elif state == 3: # 选对 → 淡绿色
|
||||||
|
self.buttons[i].config(text=full_text, style="Correct.TButton")
|
||||||
|
else: # 未选且错误 → 不变(保持原始或默认)
|
||||||
|
self.buttons[i].config(text=full_text, style="Default.TButton")
|
||||||
|
|
||||||
|
self.c_backend.update(self.row[0], correct)
|
||||||
|
self.winfo_toplevel().focus_set()
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self.my_answer = [0, 0, 0, 0]
|
||||||
|
self.correct_answer = []
|
||||||
|
self.flag = False
|
||||||
|
self.button_disable = False
|
||||||
|
|
||||||
|
self.row = self.c_backend.get_question()
|
||||||
|
|
||||||
|
if self.row == None:
|
||||||
|
print("没有了")
|
||||||
|
return
|
||||||
|
# 判断题处理
|
||||||
|
elif self.row[6] == "" and self.row[7] == "":
|
||||||
|
self.row[6] = "正确"
|
||||||
|
self.row[7] = "错误"
|
||||||
|
self.buttons[2].config(state="disabled")
|
||||||
|
self.buttons[3].config(state="disabled")
|
||||||
|
else:
|
||||||
|
self.buttons[2].config(state="normal")
|
||||||
|
self.buttons[3].config(state="normal")
|
||||||
|
|
||||||
|
# 更新题目
|
||||||
|
self.text1.config(state="normal")
|
||||||
|
self.text1.delete("1.0", "end")
|
||||||
|
self.text1.insert("end", self.row[4] + "\n" + self.row[5])
|
||||||
|
self.text1.config(state="disabled")
|
||||||
|
|
||||||
|
# 清空解析
|
||||||
|
self.text2.config(state="normal")
|
||||||
|
self.text2.delete("1.0", "end")
|
||||||
|
self.text2.config(state="disabled")
|
||||||
|
|
||||||
|
# 混洗选项
|
||||||
|
self.shuffle_index = [6, 7, 8, 9]
|
||||||
|
random.shuffle(self.shuffle_index)
|
||||||
|
|
||||||
|
for i in range(4):
|
||||||
|
btn_text = self.row[self.shuffle_index[i]].strip()
|
||||||
|
self.buttons[i].config(
|
||||||
|
text=f" {btn_text}",
|
||||||
|
state="normal",
|
||||||
|
style="Default.TButton"
|
||||||
|
)
|
||||||
|
self.correct_answer.append(1 if self.row[self.shuffle_index[i] + 4] == 1 else 0)
|
||||||
|
self.winfo_toplevel().focus_set()
|
||||||
|
|
||||||
|
def on_button_click(self,button_number):
|
||||||
|
if self.button_disable:
|
||||||
|
return
|
||||||
|
if sum(self.correct_answer) > 1:
|
||||||
|
if self.my_answer[button_number] == 0:
|
||||||
|
self.my_answer[button_number] = 1
|
||||||
|
self.buttons[button_number].config(style="Selected.TButton")
|
||||||
|
else:
|
||||||
|
self.my_answer[button_number] = 0
|
||||||
|
self.buttons[button_number].config(style="Default.TButton")
|
||||||
|
else:
|
||||||
|
self.my_answer[button_number] = 1
|
||||||
|
self._show_result()
|
||||||
|
self.winfo_toplevel().focus_set()
|
||||||
|
|
||||||
|
def on_ok_click(self):
|
||||||
|
if self.flag:
|
||||||
|
self.init()
|
||||||
|
else:
|
||||||
|
self. _show_result()
|
||||||
|
self.winfo_toplevel().focus_set()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from backend import Backend
|
||||||
|
root = tk.Tk()
|
||||||
|
font = Fonts()
|
||||||
|
c_backend = Backend()
|
||||||
|
app = Page0(root,font,c_backend)
|
||||||
|
root.mainloop()
|
||||||
105
components/page1.py
Normal file
105
components/page1.py
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import tkinter as tk
|
||||||
|
from tkinter import ttk
|
||||||
|
from components import *
|
||||||
|
|
||||||
|
|
||||||
|
class Page1:
|
||||||
|
def __init__(self, root, top_options, bottom_options):
|
||||||
|
self.root = root
|
||||||
|
self.top_options = top_options
|
||||||
|
self.bottom_options = bottom_options
|
||||||
|
|
||||||
|
# ========== 顶部复选框区域 ==========
|
||||||
|
self.top_frame = tk.Frame(self.root, bg=BG_COLOR, pady=25)
|
||||||
|
self.top_frame.pack(side=tk.TOP, fill=tk.X)
|
||||||
|
|
||||||
|
self.top_vars = []
|
||||||
|
for option in top_options:
|
||||||
|
var = tk.BooleanVar(value=True)
|
||||||
|
chk = ttk.Checkbutton(self.top_frame, text=option, variable=var, style="Custom.TCheckbutton")
|
||||||
|
chk.pack(side=tk.LEFT, padx=20)
|
||||||
|
self.top_vars.append(var)
|
||||||
|
|
||||||
|
# ========== 分割线 ==========
|
||||||
|
separator = tk.Frame(self.root, height=1, bg="black")
|
||||||
|
separator.pack(side=tk.TOP, fill=tk.X, padx=20)
|
||||||
|
|
||||||
|
# ========== 底部复选框区域 ==========
|
||||||
|
self.bottom_frame = tk.Frame(self.root, bg=BG_COLOR, pady=25)
|
||||||
|
self.bottom_frame.pack(side=tk.TOP, fill=tk.X)
|
||||||
|
|
||||||
|
self.bottom_vars = []
|
||||||
|
for option in bottom_options:
|
||||||
|
var = tk.BooleanVar(value=True)
|
||||||
|
chk = ttk.Checkbutton(self.bottom_frame, text=option, variable=var, style="Custom.TCheckbutton")
|
||||||
|
chk.pack(side=tk.LEFT, padx=20)
|
||||||
|
self.bottom_vars.append(var)
|
||||||
|
|
||||||
|
# ========== 说明文本区域 ==========
|
||||||
|
text_frame = tk.Frame(self.root, bg=BG_COLOR)
|
||||||
|
text_frame.pack(side=tk.TOP, padx=40, pady=10, fill=tk.X)
|
||||||
|
|
||||||
|
text_widget = tk.Text(
|
||||||
|
text_frame,
|
||||||
|
height=6,
|
||||||
|
width=60,
|
||||||
|
wrap=tk.WORD,
|
||||||
|
bg=BG_COLOR,
|
||||||
|
fg=FG_COLOR,
|
||||||
|
relief=tk.FLAT,
|
||||||
|
borderwidth=0,
|
||||||
|
font=(FONT_FAMILY, 12),
|
||||||
|
padx=10,
|
||||||
|
pady=10
|
||||||
|
)
|
||||||
|
text_widget.pack(expand=True, fill=tk.X)
|
||||||
|
text_widget.insert(tk.END, instructions)
|
||||||
|
text_widget.config(state=tk.DISABLED)
|
||||||
|
|
||||||
|
def setup_styles(self):
|
||||||
|
"""设置 ttk 样式"""
|
||||||
|
|
||||||
|
|
||||||
|
def top_selected(self):
|
||||||
|
"""返回第一行中被选中的项"""
|
||||||
|
return [opt for opt, var in zip(self.top_options, self.top_vars) if var.get()]
|
||||||
|
|
||||||
|
def bottom_selected(self):
|
||||||
|
"""返回第二行中被选中的项"""
|
||||||
|
return [opt for opt, var in zip(self.bottom_options, self.bottom_vars) if var.get()]
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
top_options = ["1", "2", "3", "4"]
|
||||||
|
bottom_options = ["全部的题", "未做过的题", "错过的题", ]
|
||||||
|
|
||||||
|
root = tk.Tk()
|
||||||
|
app = Page1(root, top_options, bottom_options)
|
||||||
|
|
||||||
|
# 按钮区域
|
||||||
|
button_frame = tk.Frame(root)
|
||||||
|
button_frame.pack(side=tk.TOP, pady=20)
|
||||||
|
|
||||||
|
# 打印上一行选中项的按钮
|
||||||
|
btn_print_top = tk.Button(
|
||||||
|
button_frame,
|
||||||
|
text="1",
|
||||||
|
command=lambda: print(app.top_selected()),
|
||||||
|
font=("Helvetica", 14),
|
||||||
|
bg="lightblue",
|
||||||
|
width=20
|
||||||
|
)
|
||||||
|
btn_print_top.pack(side=tk.LEFT, padx=10)
|
||||||
|
|
||||||
|
# 打印下一行选中项的按钮
|
||||||
|
btn_print_bottom = tk.Button(
|
||||||
|
button_frame,
|
||||||
|
text="2",
|
||||||
|
command=lambda: print(app.bottom_selected()),
|
||||||
|
font=("Helvetica", 14),
|
||||||
|
bg="lightgreen",
|
||||||
|
width=20
|
||||||
|
)
|
||||||
|
btn_print_bottom.pack(side=tk.LEFT, padx=10)
|
||||||
|
|
||||||
|
root.mainloop()
|
||||||
98
components/page2.py
Normal file
98
components/page2.py
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# 统计数据页
|
||||||
|
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import ttk
|
||||||
|
|
||||||
|
class Page2:
|
||||||
|
def __init__(self, root):
|
||||||
|
self.root = root
|
||||||
|
|
||||||
|
# 创建主框架
|
||||||
|
main_frame = ttk.Frame(root, padding="20")
|
||||||
|
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||||
|
main_frame.columnconfigure(0, weight=1)
|
||||||
|
main_frame.rowconfigure(1, weight=1)
|
||||||
|
|
||||||
|
# 标题
|
||||||
|
title_label = ttk.Label(main_frame, text="当前数据统计", font=("Arial", 16, "bold"))
|
||||||
|
title_label.grid(row=0, column=0, columnspan=4, pady=(0, 20))
|
||||||
|
|
||||||
|
# === 关键指标区域 ===
|
||||||
|
self.kpi_data = []
|
||||||
|
|
||||||
|
# KPI 容器
|
||||||
|
self.kpi_container = ttk.Frame(main_frame)
|
||||||
|
self.kpi_container.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||||
|
for i in range(4):
|
||||||
|
self.kpi_container.columnconfigure(i, weight=1)
|
||||||
|
|
||||||
|
self.value_labels = []
|
||||||
|
self.create_dashboard_units_4col()
|
||||||
|
|
||||||
|
def create_kpi_unit(self, parent, name, value, row, col):
|
||||||
|
"""创建单个单元"""
|
||||||
|
unit_frame = ttk.Frame(parent, relief="solid", borderwidth=1, padding=10)
|
||||||
|
unit_frame.grid(row=row, column=col, padx=8, pady=8, sticky="nsew")
|
||||||
|
|
||||||
|
# 设置行权重,使内容垂直居中
|
||||||
|
parent.rowconfigure(row, weight=1)
|
||||||
|
|
||||||
|
if name and value:
|
||||||
|
name_label = ttk.Label(unit_frame, text=f"{name}:", font=("Arial", 11))
|
||||||
|
name_label.pack(anchor="w")
|
||||||
|
value_label = ttk.Label(unit_frame, text=value, font=("Arial", 11, "bold"))
|
||||||
|
value_label.pack(anchor="e")
|
||||||
|
return value_label
|
||||||
|
else:
|
||||||
|
# 如果 name 或 value 为空,显示占位符或空白
|
||||||
|
ttk.Label(unit_frame, text="—", foreground="gray").pack()
|
||||||
|
return None
|
||||||
|
|
||||||
|
def create_dashboard_units_4col(self):
|
||||||
|
"""创建4列布局的单元"""
|
||||||
|
rows = (len(self.kpi_data) + 3) // 4 # 向上取整
|
||||||
|
|
||||||
|
for r in range(rows):
|
||||||
|
self.kpi_container.rowconfigure(r, weight=1)
|
||||||
|
|
||||||
|
for idx, item in enumerate(self.kpi_data):
|
||||||
|
row_idx = idx // 4
|
||||||
|
col_idx = idx % 4
|
||||||
|
|
||||||
|
if item and len(item) >= 2:
|
||||||
|
name, value = item[0], item[1]
|
||||||
|
else:
|
||||||
|
name, value = None, None
|
||||||
|
value_label = self.create_kpi_unit(self.kpi_container, name, value, row_idx, col_idx)
|
||||||
|
self.value_labels.clear()
|
||||||
|
self.value_labels.append(value_label)
|
||||||
|
|
||||||
|
def update_data(self, new_values):
|
||||||
|
self.kpi_data = new_values
|
||||||
|
self.create_dashboard_units_4col()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import backend
|
||||||
|
import random
|
||||||
|
# 实例化后端
|
||||||
|
c_backend = backend.Backend()
|
||||||
|
|
||||||
|
# 创建 GUI
|
||||||
|
root = tk.Tk()
|
||||||
|
root.title("统计面板")
|
||||||
|
root.geometry("800x600")
|
||||||
|
app = Page2(root)
|
||||||
|
|
||||||
|
def on_refresh():
|
||||||
|
s = c_backend.get_statistics()
|
||||||
|
for v in s:
|
||||||
|
if v is not None and len(v) >= 2:
|
||||||
|
v[1] = str(random.randint(1,100))
|
||||||
|
app.update_data(s)
|
||||||
|
|
||||||
|
# 刷新按钮
|
||||||
|
refresh_btn = ttk.Button(root, text="刷新数据", command=on_refresh)
|
||||||
|
refresh_btn.grid(row=2, column=0, pady=10)
|
||||||
|
|
||||||
|
root.mainloop()
|
||||||
388
crawler/getLeCheng_chapter.ipynb
Normal file
388
crawler/getLeCheng_chapter.ipynb
Normal file
@@ -0,0 +1,388 @@
|
|||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"id": "initial_id",
|
||||||
|
"metadata": {
|
||||||
|
"collapsed": true,
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T01:03:22.758571Z",
|
||||||
|
"start_time": "2025-08-19T01:03:22.753008Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"source": [
|
||||||
|
"import time\n",
|
||||||
|
"\n",
|
||||||
|
"from selenium import webdriver\n",
|
||||||
|
"from selenium.webdriver.edge.service import Service\n",
|
||||||
|
"from selenium.webdriver.common.by import By\n",
|
||||||
|
"from selenium.webdriver.support.ui import WebDriverWait\n",
|
||||||
|
"from selenium.webdriver.support import expected_conditions as EC\n",
|
||||||
|
"from selenium.webdriver.edge.options import Options"
|
||||||
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 49
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T01:03:23.224371Z",
|
||||||
|
"start_time": "2025-08-19T01:03:23.220216Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"from bs4 import BeautifulSoup\n",
|
||||||
|
"import sqlite3"
|
||||||
|
],
|
||||||
|
"id": "59b26d9f105eae85",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 50
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T01:03:25.179818Z",
|
||||||
|
"start_time": "2025-08-19T01:03:25.173558Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": "db_path = '../data.db'",
|
||||||
|
"id": "37a70656848ceced",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 51
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T01:03:25.713012Z",
|
||||||
|
"start_time": "2025-08-19T01:03:25.704775Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"conn = sqlite3.connect(db_path)\n",
|
||||||
|
"conn.execute('''CREATE TABLE \"questions\"\n",
|
||||||
|
"(\n",
|
||||||
|
" id INTEGER\n",
|
||||||
|
" constraint questions_pk\n",
|
||||||
|
" primary key autoincrement,\n",
|
||||||
|
" title TEXT,\n",
|
||||||
|
" chapter TEXT,\n",
|
||||||
|
" q_num text,\n",
|
||||||
|
" q_type text,\n",
|
||||||
|
" question TEXT not null,\n",
|
||||||
|
" a TEXT not null,\n",
|
||||||
|
" b TEXT not null,\n",
|
||||||
|
" c TEXT not null,\n",
|
||||||
|
" d TEXT not null,\n",
|
||||||
|
" a_result BLOB default false,\n",
|
||||||
|
" b_result BLOB default false,\n",
|
||||||
|
" c_result BLOB default false,\n",
|
||||||
|
" d_result BLOB default false,\n",
|
||||||
|
" explanation TEXT,\n",
|
||||||
|
" count integer default 3 not null\n",
|
||||||
|
")''')\n",
|
||||||
|
"\n",
|
||||||
|
"conn.execute('''CREATE TABLE \"answers_history\"\n",
|
||||||
|
"(\n",
|
||||||
|
" id INTEGER not null\n",
|
||||||
|
" constraint answers_history__questions_id_fk\n",
|
||||||
|
" references questions,\n",
|
||||||
|
" time_used INTEGER,\n",
|
||||||
|
" state INTEGER,\n",
|
||||||
|
" time text default CURRENT_TIMESTAMP\n",
|
||||||
|
")''')\n",
|
||||||
|
"\n",
|
||||||
|
"conn.execute('''CREATE TABLE url\n",
|
||||||
|
" (\n",
|
||||||
|
" id INTEGER not null,\n",
|
||||||
|
" url TEXT\n",
|
||||||
|
" )''')\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"conn.commit()\n"
|
||||||
|
],
|
||||||
|
"id": "d70a270099e8b056",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 52
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T01:03:27.430817Z",
|
||||||
|
"start_time": "2025-08-19T01:03:27.423603Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"edge_options = Options()\n",
|
||||||
|
"#edge_options.add_argument(\"--headless\") # 可选:无界面模式\n",
|
||||||
|
"edge_options.add_argument(\"--disable-gpu\")\n",
|
||||||
|
"edge_options.add_argument(\"--no-sandbox\")\n",
|
||||||
|
"edge_options.add_argument(\"--disable-extensions\")\n",
|
||||||
|
"edge_options.add_argument(\"--disable-plugins\")\n",
|
||||||
|
"edge_options.add_argument(\"--disable-popup-blocking\")\n",
|
||||||
|
"edge_options.add_argument(\"--disable-infobars\")\n",
|
||||||
|
"edge_options.add_argument(\"--disable-notifications\")\n",
|
||||||
|
"edge_options.add_argument(\"--no-first-run\")\n",
|
||||||
|
"edge_options.add_argument(\"--no-default-browser-check\")\n",
|
||||||
|
"\n",
|
||||||
|
"user_data_dir = r\"D:\\code\\edge\"\n",
|
||||||
|
"edge_options.add_argument(f\"--user-data-dir={user_data_dir}\")\n",
|
||||||
|
"# 指定配置文件(可选,默认是 Default)\n",
|
||||||
|
"edge_options.add_argument(\"--profile-directory=Default\")"
|
||||||
|
],
|
||||||
|
"id": "e4a35062c4549f44",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 53
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T01:03:30.978615Z",
|
||||||
|
"start_time": "2025-08-19T01:03:28.414779Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"# 指定 EdgeDriver 路径(可选,若已配置环境变量可省略)\n",
|
||||||
|
"service = Service(executable_path=r\"D:\\app\\edgeDriver\\msedgedriver.exe\")\n",
|
||||||
|
"# 创建 Edge 浏览器实例\n",
|
||||||
|
"driver = webdriver.Edge(service=service, options=edge_options)"
|
||||||
|
],
|
||||||
|
"id": "9b48ddaca80598aa",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 54
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T01:00:42.177993Z",
|
||||||
|
"start_time": "2025-08-19T01:00:42.171173Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"def get_web(url):\n",
|
||||||
|
" driver.get(url)\n",
|
||||||
|
"\n",
|
||||||
|
" # 等待页面渲染完成(例如等待 body 加载)\n",
|
||||||
|
" wait = WebDriverWait(driver, 720)\n",
|
||||||
|
" wait.until(EC.presence_of_element_located((By.TAG_NAME, \"body\")))\n",
|
||||||
|
" time.sleep(3)\n",
|
||||||
|
"\n",
|
||||||
|
" #进入背题模式\n",
|
||||||
|
" clickable_element = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, \".setting-type.iconfont.icon-setting\")))\n",
|
||||||
|
" clickable_element.click()\n",
|
||||||
|
" wait.until(\n",
|
||||||
|
" EC.element_to_be_clickable((By.CSS_SELECTOR, \".question-setting-button.ant-btn.ant-btn-default\"))).click()\n",
|
||||||
|
"\n",
|
||||||
|
" # 获取渲染后的 HTML\n",
|
||||||
|
" rendered_html = driver.page_source\n",
|
||||||
|
" return rendered_html"
|
||||||
|
],
|
||||||
|
"id": "2b02063fec8abbdd",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 43
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T01:00:42.212116Z",
|
||||||
|
"start_time": "2025-08-19T01:00:42.206610Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"def list_get(lst, index, default=\"\"):\n",
|
||||||
|
" try:\n",
|
||||||
|
" return lst[index]\n",
|
||||||
|
" except IndexError:\n",
|
||||||
|
" return default"
|
||||||
|
],
|
||||||
|
"id": "de9650bb0e005d4a",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 44
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T01:00:42.247110Z",
|
||||||
|
"start_time": "2025-08-19T01:00:42.237114Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"def write2db(rendered_html, info):\n",
|
||||||
|
" # 解析web并登记\n",
|
||||||
|
" soup = BeautifulSoup(rendered_html, 'html.parser')\n",
|
||||||
|
" full_left = soup.find_all('div', class_='pull-left')\n",
|
||||||
|
" for questions in full_left:\n",
|
||||||
|
" for question in questions.children:\n",
|
||||||
|
" title_info = [] #num,type,question\n",
|
||||||
|
" answers_info = []\n",
|
||||||
|
" answers_correct_info = []\n",
|
||||||
|
" explain_info = \"\"\n",
|
||||||
|
"\n",
|
||||||
|
" # 标题信息\n",
|
||||||
|
" for title in question.find_all('div', class_='p-stem'):\n",
|
||||||
|
" for element in title.children:\n",
|
||||||
|
" title_info.append(element.text)\n",
|
||||||
|
"\n",
|
||||||
|
" # 题目信息\n",
|
||||||
|
" for answer in question.find_all('div', class_='answer-ul'):\n",
|
||||||
|
" for element in answer.find_all(\"div\", recursive=False):\n",
|
||||||
|
" # 答案\n",
|
||||||
|
" if \"answer\" in element.get(\"class\"):\n",
|
||||||
|
" answers_correct_info.append(True)\n",
|
||||||
|
" else:\n",
|
||||||
|
" answers_correct_info.append(False)\n",
|
||||||
|
"\n",
|
||||||
|
" # 问题\n",
|
||||||
|
" text_elements = element.select(\"div > div > div > div > p\")\n",
|
||||||
|
" for text_element in text_elements:\n",
|
||||||
|
" if text_element.text is not None and text_element.text != \"\":\n",
|
||||||
|
" answers_info.append(text_element.text)\n",
|
||||||
|
"\n",
|
||||||
|
" # 解析\n",
|
||||||
|
" for explain in question.find_all('div', class_='practise-answer-text'):\n",
|
||||||
|
" explain_info += str(explain.get_text(strip=True))\n",
|
||||||
|
"\n",
|
||||||
|
" cursor = conn.execute(\n",
|
||||||
|
" \"INSERT INTO questions (title, chapter, q_num, q_type, question, a, b, c, d, a_result, b_result, c_result, d_result, explanation) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\",\n",
|
||||||
|
" (\n",
|
||||||
|
" info[1],\n",
|
||||||
|
" info[2],\n",
|
||||||
|
" list_get(title_info,0),\n",
|
||||||
|
" list_get(title_info,1),\n",
|
||||||
|
" list_get(title_info,2),\n",
|
||||||
|
" list_get(answers_info,0),\n",
|
||||||
|
" list_get(answers_info,1),\n",
|
||||||
|
" list_get(answers_info,2),\n",
|
||||||
|
" list_get(answers_info,3),\n",
|
||||||
|
" list_get(answers_correct_info,0,False),\n",
|
||||||
|
" list_get(answers_correct_info,1,False),\n",
|
||||||
|
" list_get(answers_correct_info,2,False),\n",
|
||||||
|
" list_get(answers_correct_info,3,False),\n",
|
||||||
|
" explain_info,\n",
|
||||||
|
" )\n",
|
||||||
|
" )\n",
|
||||||
|
" inserted_id = cursor.lastrowid\n",
|
||||||
|
" conn.execute(\n",
|
||||||
|
" \"INSERT INTO url (id, url) VALUES (?, ?)\",\n",
|
||||||
|
" (inserted_id, info[0], )\n",
|
||||||
|
" )\n",
|
||||||
|
" conn.commit()"
|
||||||
|
],
|
||||||
|
"id": "c28a23cbd84f6ea0",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 45
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T01:00:42.273569Z",
|
||||||
|
"start_time": "2025-08-19T01:00:42.265569Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"bg_infos = [\n",
|
||||||
|
" [\"期货乐橙章节练习\", 1, 1414, 1],\n",
|
||||||
|
" [\"期货乐橙章节练习\", 2, 1419, 3],\n",
|
||||||
|
" [\"期货乐橙章节练习\", 3, 1448, 2],\n",
|
||||||
|
" [\"期货乐橙章节练习\", 4, 1485, 2],\n",
|
||||||
|
" [\"期货乐橙章节练习\", 5, 1523, 2],\n",
|
||||||
|
" [\"期货乐橙章节练习\", 6, 1543, 2],\n",
|
||||||
|
"]"
|
||||||
|
],
|
||||||
|
"id": "f8ed3be15b2a69a7",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 46
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T01:00:42.311569Z",
|
||||||
|
"start_time": "2025-08-19T01:00:42.302568Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"def main():\n",
|
||||||
|
" try:\n",
|
||||||
|
" for bg_info in bg_infos:\n",
|
||||||
|
" for index in range(1, bg_info[3]+1):\n",
|
||||||
|
" url = f\"https://www.bestlec.com/practise/practise?title=%E9%A1%BA%E5%BA%8F%E7%BB%83%E4%B9%A0&qBankId=39&qBankTitle=%E3%80%90%E6%9C%9F%E8%B4%A7%E6%B3%95%E8%A7%84%E3%80%91%E7%AB%A0%E8%8A%82%E7%BB%83%E4%B9%A0&chapterId={bg_info[2]}&practise=1&type=practise&selectSec={index}\"\n",
|
||||||
|
" rendered_html = get_web(url)\n",
|
||||||
|
" write2db(rendered_html, [url, bg_info[0], bg_info[1]])\n",
|
||||||
|
" except Exception as e:\n",
|
||||||
|
" print(\"error: \" + e)\n",
|
||||||
|
" finally:\n",
|
||||||
|
" try:\n",
|
||||||
|
" conn.close()\n",
|
||||||
|
" except Exception as e:\n",
|
||||||
|
" print(\"db:\", e)\n",
|
||||||
|
"\n",
|
||||||
|
" try:\n",
|
||||||
|
" driver.quit()\n",
|
||||||
|
" except Exception as e:\n",
|
||||||
|
" print(\"brother:\", e)"
|
||||||
|
],
|
||||||
|
"id": "fcfc560b46c29aaa",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 47
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T01:01:51.670165Z",
|
||||||
|
"start_time": "2025-08-19T01:00:42.337618Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"if __name__ == '__main__':\n",
|
||||||
|
" main()"
|
||||||
|
],
|
||||||
|
"id": "811c9d3647c46f8b",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 48
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T01:01:51.740128Z",
|
||||||
|
"start_time": "2025-08-19T01:01:51.737199Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": "",
|
||||||
|
"id": "5224515d66fe0b",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "Python 3",
|
||||||
|
"language": "python",
|
||||||
|
"name": "python3"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": {
|
||||||
|
"name": "ipython",
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"file_extension": ".py",
|
||||||
|
"mimetype": "text/x-python",
|
||||||
|
"name": "python",
|
||||||
|
"nbconvert_exporter": "python",
|
||||||
|
"pygments_lexer": "ipython2",
|
||||||
|
"version": "2.7.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 5
|
||||||
|
}
|
||||||
415
crawler/getLeCheng_exam.ipynb
Normal file
415
crawler/getLeCheng_exam.ipynb
Normal file
@@ -0,0 +1,415 @@
|
|||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"id": "initial_id",
|
||||||
|
"metadata": {
|
||||||
|
"collapsed": true,
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T02:05:36.929261Z",
|
||||||
|
"start_time": "2025-08-19T02:05:36.924419Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"source": [
|
||||||
|
"import time\n",
|
||||||
|
"\n",
|
||||||
|
"from selenium import webdriver\n",
|
||||||
|
"from selenium.webdriver.edge.service import Service\n",
|
||||||
|
"from selenium.webdriver.common.by import By\n",
|
||||||
|
"from selenium.webdriver.support.ui import WebDriverWait\n",
|
||||||
|
"from selenium.webdriver.support import expected_conditions as EC\n",
|
||||||
|
"from selenium.webdriver.edge.options import Options"
|
||||||
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 86
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T02:05:37.477084Z",
|
||||||
|
"start_time": "2025-08-19T02:05:37.467743Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"from bs4 import BeautifulSoup\n",
|
||||||
|
"import sqlite3"
|
||||||
|
],
|
||||||
|
"id": "59b26d9f105eae85",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 87
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T02:05:38.051715Z",
|
||||||
|
"start_time": "2025-08-19T02:05:38.047420Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": "db_path = '../data.db'",
|
||||||
|
"id": "37a70656848ceced",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 88
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T02:05:38.522754Z",
|
||||||
|
"start_time": "2025-08-19T02:05:38.515572Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"conn = sqlite3.connect(db_path)\n",
|
||||||
|
"conn.execute('''CREATE TABLE \"questions\"\n",
|
||||||
|
"(\n",
|
||||||
|
" id INTEGER\n",
|
||||||
|
" constraint questions_pk\n",
|
||||||
|
" primary key autoincrement,\n",
|
||||||
|
" title TEXT,\n",
|
||||||
|
" chapter TEXT,\n",
|
||||||
|
" q_num text,\n",
|
||||||
|
" q_type text,\n",
|
||||||
|
" question TEXT not null,\n",
|
||||||
|
" a TEXT not null,\n",
|
||||||
|
" b TEXT not null,\n",
|
||||||
|
" c TEXT not null,\n",
|
||||||
|
" d TEXT not null,\n",
|
||||||
|
" a_result BLOB default false,\n",
|
||||||
|
" b_result BLOB default false,\n",
|
||||||
|
" c_result BLOB default false,\n",
|
||||||
|
" d_result BLOB default false,\n",
|
||||||
|
" explanation TEXT,\n",
|
||||||
|
" count integer default 3 not null\n",
|
||||||
|
")''')\n",
|
||||||
|
"\n",
|
||||||
|
"conn.execute('''CREATE TABLE \"answers_history\"\n",
|
||||||
|
"(\n",
|
||||||
|
" id INTEGER not null\n",
|
||||||
|
" constraint answers_history__questions_id_fk\n",
|
||||||
|
" references questions,\n",
|
||||||
|
" time_used INTEGER,\n",
|
||||||
|
" state INTEGER,\n",
|
||||||
|
" time text default CURRENT_TIMESTAMP\n",
|
||||||
|
")''')\n",
|
||||||
|
"\n",
|
||||||
|
"conn.execute('''CREATE TABLE url\n",
|
||||||
|
" (\n",
|
||||||
|
" id INTEGER not null,\n",
|
||||||
|
" url TEXT\n",
|
||||||
|
" )''')\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"conn.commit()\n"
|
||||||
|
],
|
||||||
|
"id": "d70a270099e8b056",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 89
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T02:05:39.660112Z",
|
||||||
|
"start_time": "2025-08-19T02:05:39.654188Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"edge_options = Options()\n",
|
||||||
|
"#edge_options.add_argument(\"--headless\") # 可选:无界面模式\n",
|
||||||
|
"edge_options.add_argument(\"--disable-gpu\")\n",
|
||||||
|
"edge_options.add_argument(\"--no-sandbox\")\n",
|
||||||
|
"edge_options.add_argument(\"--disable-extensions\")\n",
|
||||||
|
"edge_options.add_argument(\"--disable-plugins\")\n",
|
||||||
|
"edge_options.add_argument(\"--disable-popup-blocking\")\n",
|
||||||
|
"edge_options.add_argument(\"--disable-infobars\")\n",
|
||||||
|
"edge_options.add_argument(\"--disable-notifications\")\n",
|
||||||
|
"edge_options.add_argument(\"--no-first-run\")\n",
|
||||||
|
"edge_options.add_argument(\"--no-default-browser-check\")\n",
|
||||||
|
"\n",
|
||||||
|
"user_data_dir = r\"D:\\code\\edge\"\n",
|
||||||
|
"edge_options.add_argument(f\"--user-data-dir={user_data_dir}\")\n",
|
||||||
|
"# 指定配置文件(可选,默认是 Default)\n",
|
||||||
|
"edge_options.add_argument(\"--profile-directory=Default\")"
|
||||||
|
],
|
||||||
|
"id": "e4a35062c4549f44",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 90
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T02:05:42.646764Z",
|
||||||
|
"start_time": "2025-08-19T02:05:40.137721Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"# 指定 EdgeDriver 路径(可选,若已配置环境变量可省略)\n",
|
||||||
|
"service = Service(executable_path=r\"D:\\app\\edgeDriver\\msedgedriver.exe\")\n",
|
||||||
|
"# 创建 Edge 浏览器实例\n",
|
||||||
|
"driver = webdriver.Edge(service=service, options=edge_options)"
|
||||||
|
],
|
||||||
|
"id": "9b48ddaca80598aa",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 91
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T01:58:29.792348Z",
|
||||||
|
"start_time": "2025-08-19T01:58:29.786681Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"def get_web(url):\n",
|
||||||
|
" driver.get(url)\n",
|
||||||
|
"\n",
|
||||||
|
" # 等待页面渲染完成(例如等待 body 加载)\n",
|
||||||
|
" wait = WebDriverWait(driver, 720)\n",
|
||||||
|
" wait.until(EC.presence_of_element_located((By.TAG_NAME, \"body\")))\n",
|
||||||
|
" time.sleep(3)\n",
|
||||||
|
"\n",
|
||||||
|
" #进入背题模式\n",
|
||||||
|
" # clickable_element = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, \".setting-type.iconfont.icon-setting\")))\n",
|
||||||
|
" # clickable_element.click()\n",
|
||||||
|
" # wait.until(\n",
|
||||||
|
" # EC.element_to_be_clickable((By.CSS_SELECTOR, \".question-setting-button.ant-btn.ant-btn-default\"))).click()\n",
|
||||||
|
"\n",
|
||||||
|
" # 获取渲染后的 HTML\n",
|
||||||
|
" rendered_html = driver.page_source\n",
|
||||||
|
" return rendered_html"
|
||||||
|
],
|
||||||
|
"id": "2b02063fec8abbdd",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T01:58:30.323362Z",
|
||||||
|
"start_time": "2025-08-19T01:58:30.317695Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"def list_get(lst, index, default=\"\"):\n",
|
||||||
|
" try:\n",
|
||||||
|
" return lst[index]\n",
|
||||||
|
" except IndexError:\n",
|
||||||
|
" return default"
|
||||||
|
],
|
||||||
|
"id": "de9650bb0e005d4a",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 81
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T01:58:30.884914Z",
|
||||||
|
"start_time": "2025-08-19T01:58:30.871279Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"def write2db(rendered_html, info):\n",
|
||||||
|
" # 解析web并登记\n",
|
||||||
|
" soup = BeautifulSoup(rendered_html, 'html.parser')\n",
|
||||||
|
" full_left = soup.find_all('div', class_='pull-left')\n",
|
||||||
|
"\n",
|
||||||
|
" for questions in full_left:\n",
|
||||||
|
" for question in questions.children:\n",
|
||||||
|
" title_info = [] #num,type,question\n",
|
||||||
|
" answers_info = []\n",
|
||||||
|
" answers_correct_info = [False,False,False,False,]\n",
|
||||||
|
" explain_info = \"\"\n",
|
||||||
|
"\n",
|
||||||
|
" # 标题信息\n",
|
||||||
|
" for title in question.find_all('div', class_='p-stem'):\n",
|
||||||
|
" for element in title.children:\n",
|
||||||
|
" title_info.append(element.text)\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
" # 题目信息\n",
|
||||||
|
" for answer in question.find_all('div', class_='answerClass'):\n",
|
||||||
|
" # 答案\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
" # 带选项\n",
|
||||||
|
" text_elements = answer.select(\"div > div > p\")\n",
|
||||||
|
" for text_element in text_elements:\n",
|
||||||
|
" if text_element.text is not None and text_element.text != \"\":\n",
|
||||||
|
" answers_info.append(text_element.text)\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
" # 解析\n",
|
||||||
|
" explains = question.find_all('div', class_='practise-answer-text')\n",
|
||||||
|
"\n",
|
||||||
|
" try:\n",
|
||||||
|
" for i in explains[0].text:\n",
|
||||||
|
" match i:\n",
|
||||||
|
" case \"A\":\n",
|
||||||
|
" answers_correct_info[0] = True\n",
|
||||||
|
" case \"B\":\n",
|
||||||
|
" answers_correct_info[1] = True\n",
|
||||||
|
" case \"C\":\n",
|
||||||
|
" answers_correct_info[2] = True\n",
|
||||||
|
" case \"D\":\n",
|
||||||
|
" answers_correct_info[3] = True\n",
|
||||||
|
" case _ :\n",
|
||||||
|
" print(i)\n",
|
||||||
|
" pass\n",
|
||||||
|
" except Exception as e:\n",
|
||||||
|
" # e.with_traceback()\n",
|
||||||
|
" # print(title_info)\n",
|
||||||
|
" pass\n",
|
||||||
|
"\n",
|
||||||
|
" if len(explains[0]) == 0:\n",
|
||||||
|
" print(\"0 answers found!!\", title_info)\n",
|
||||||
|
"\n",
|
||||||
|
" for explain in explains:\n",
|
||||||
|
" explain_info += str(explain.get_text(strip=True))\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
" cursor = conn.execute(\n",
|
||||||
|
" \"INSERT INTO questions (title, chapter, q_num, q_type, question, a, b, c, d, a_result, b_result, c_result, d_result, explanation) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\",\n",
|
||||||
|
" (\n",
|
||||||
|
" info[1],\n",
|
||||||
|
" info[2],\n",
|
||||||
|
" list_get(title_info,0),\n",
|
||||||
|
" list_get(title_info,1),\n",
|
||||||
|
" list_get(title_info,2),\n",
|
||||||
|
" list_get(answers_info,0),\n",
|
||||||
|
" list_get(answers_info,1),\n",
|
||||||
|
" list_get(answers_info,2),\n",
|
||||||
|
" list_get(answers_info,3),\n",
|
||||||
|
" list_get(answers_correct_info,0,False),\n",
|
||||||
|
" list_get(answers_correct_info,1,False),\n",
|
||||||
|
" list_get(answers_correct_info,2,False),\n",
|
||||||
|
" list_get(answers_correct_info,3,False),\n",
|
||||||
|
" explain_info,\n",
|
||||||
|
" )\n",
|
||||||
|
" )\n",
|
||||||
|
" inserted_id = cursor.lastrowid\n",
|
||||||
|
" conn.execute(\n",
|
||||||
|
" \"INSERT INTO url (id, url) VALUES (?, ?)\",\n",
|
||||||
|
" (inserted_id, info[0], )\n",
|
||||||
|
" )\n",
|
||||||
|
" conn.commit()"
|
||||||
|
],
|
||||||
|
"id": "c28a23cbd84f6ea0",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 82
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T01:58:31.409810Z",
|
||||||
|
"start_time": "2025-08-19T01:58:31.404300Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"ttt = \"期货乐橙假题\"\n",
|
||||||
|
"bg_infos = [\n",
|
||||||
|
" [ttt, 0, 1385, 1],\n",
|
||||||
|
" [ttt, 0, 1392, 1],\n",
|
||||||
|
" [ttt, 0, 1393, 1],\n",
|
||||||
|
" [ttt, 0, 1394, 1],\n",
|
||||||
|
" [ttt, 0, 1395, 1],\n",
|
||||||
|
"]"
|
||||||
|
],
|
||||||
|
"id": "f8ed3be15b2a69a7",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 83
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T01:58:32.106767Z",
|
||||||
|
"start_time": "2025-08-19T01:58:32.099456Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"def main():\n",
|
||||||
|
" try:\n",
|
||||||
|
" for bg_info in bg_infos:\n",
|
||||||
|
" for index in range(1, bg_info[3]+1):\n",
|
||||||
|
" #url = f\"https://www.bestlec.com/practise/practise?title=%E9%A1%BA%E5%BA%8F%E7%BB%83%E4%B9%A0&qBankId=39&qBankTitle=%E3%80%90%E6%9C%9F%E8%B4%A7%E6%B3%95%E8%A7%84%E3%80%91%E7%AB%A0%E8%8A%82%E7%BB%83%E4%B9%A0&chapterId={bg_info[2]}&practise=1&type=practise&selectSec={index}\"\n",
|
||||||
|
"\n",
|
||||||
|
" url = f\"https://www.bestlec.com/practise/practise?id={bg_info[2]}&qBankTitle=%E3%80%90%E6%9C%9F%E8%B4%A7%E6%B3%95%E8%A7%84%E3%80%91%E5%8E%86%E5%B9%B4%E7%9C%9F%E9%A2%98&title=%E7%9C%9F%E9%A2%98%E8%80%83%E8%AF%95&type=test&testType=search_paper\"\n",
|
||||||
|
"\n",
|
||||||
|
" rendered_html = get_web(url)\n",
|
||||||
|
" write2db(rendered_html, [url, bg_info[0], bg_info[1]])\n",
|
||||||
|
" except Exception as e:\n",
|
||||||
|
" print(\"error: \" + e)\n",
|
||||||
|
" finally:\n",
|
||||||
|
" try:\n",
|
||||||
|
" conn.close()\n",
|
||||||
|
" except Exception as e:\n",
|
||||||
|
" print(\"db:\", e)\n",
|
||||||
|
"\n",
|
||||||
|
" try:\n",
|
||||||
|
" driver.quit()\n",
|
||||||
|
" except Exception as e:\n",
|
||||||
|
" print(\"brother:\", e)"
|
||||||
|
],
|
||||||
|
"id": "fcfc560b46c29aaa",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 84
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T01:59:02.604021Z",
|
||||||
|
"start_time": "2025-08-19T01:58:32.772829Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"if __name__ == '__main__':\n",
|
||||||
|
" main()"
|
||||||
|
],
|
||||||
|
"id": "811c9d3647c46f8b",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": 85
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-08-19T01:59:03.138625Z",
|
||||||
|
"start_time": "2025-08-19T01:59:03.135518Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": "",
|
||||||
|
"id": "5224515d66fe0b",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "Python 3",
|
||||||
|
"language": "python",
|
||||||
|
"name": "python3"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": {
|
||||||
|
"name": "ipython",
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"file_extension": ".py",
|
||||||
|
"mimetype": "text/x-python",
|
||||||
|
"name": "python",
|
||||||
|
"nbconvert_exporter": "python",
|
||||||
|
"pygments_lexer": "ipython2",
|
||||||
|
"version": "2.7.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 5
|
||||||
|
}
|
||||||
BIN
data.db.zip
Normal file
BIN
data.db.zip
Normal file
Binary file not shown.
91
main.py
Normal file
91
main.py
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import tkinter as tk
|
||||||
|
from tkinter import ttk
|
||||||
|
from backend import Backend
|
||||||
|
from components import *
|
||||||
|
|
||||||
|
# 后端
|
||||||
|
c_backend = Backend()
|
||||||
|
|
||||||
|
# 创建主窗口
|
||||||
|
root = tk.Tk()
|
||||||
|
root.title("zrx")
|
||||||
|
root.geometry("1000x900")
|
||||||
|
root.configure(bg=BG_COLOR)
|
||||||
|
|
||||||
|
# 字体文件
|
||||||
|
fonts = Fonts()
|
||||||
|
|
||||||
|
notebook = ttk.Notebook(root)
|
||||||
|
notebook.pack(fill='both', expand=True, padx=10, pady=10)
|
||||||
|
|
||||||
|
style = ttk.Style()
|
||||||
|
style.theme_use("winnative")
|
||||||
|
|
||||||
|
# 自定义复选框样式
|
||||||
|
style.configure(
|
||||||
|
"Custom.TCheckbutton",
|
||||||
|
font=(FONT_FAMILY, 14),
|
||||||
|
background=BG_COLOR,
|
||||||
|
foreground=FG_COLOR,
|
||||||
|
padding=8
|
||||||
|
)
|
||||||
|
style.map(
|
||||||
|
"Custom.TCheckbutton",
|
||||||
|
background=[("active", BG_COLOR)],
|
||||||
|
foreground=[("active", ACCENT_COLOR)]
|
||||||
|
)
|
||||||
|
|
||||||
|
# 页面0
|
||||||
|
frame0 = ttk.Frame(notebook, padding=20)
|
||||||
|
notebook.add(frame0, text="主页")
|
||||||
|
page0 = Page0(frame0,fonts,c_backend)
|
||||||
|
page0.init()
|
||||||
|
|
||||||
|
# 页面1
|
||||||
|
frame1 = ttk.Frame(notebook, padding=20)
|
||||||
|
notebook.add(frame1, text="设置")
|
||||||
|
top_options = c_backend.get_source_type()
|
||||||
|
page1 = Page1(frame1,top_options,bottom_options)
|
||||||
|
|
||||||
|
# 页面2
|
||||||
|
frame2 = ttk.Frame(notebook, padding=20)
|
||||||
|
notebook.add(frame2, text="统计")
|
||||||
|
page2 = Page2(frame2)
|
||||||
|
|
||||||
|
# 事件
|
||||||
|
def page0_event():
|
||||||
|
c_backend.reset_time()
|
||||||
|
c_backend.set_config(
|
||||||
|
page1.top_selected(),
|
||||||
|
page1.bottom_selected()
|
||||||
|
)
|
||||||
|
|
||||||
|
def page1_event():
|
||||||
|
pass
|
||||||
|
|
||||||
|
def page2_event():
|
||||||
|
r = c_backend.get_statistics()
|
||||||
|
print(r)
|
||||||
|
page2.update_data(r)
|
||||||
|
|
||||||
|
tab_handlers = [
|
||||||
|
page0_event,
|
||||||
|
page1_event,
|
||||||
|
page2_event,
|
||||||
|
]
|
||||||
|
|
||||||
|
def on_space_pressed(event):
|
||||||
|
page0.init()
|
||||||
|
|
||||||
|
def on_tab_changed(event):
|
||||||
|
try:
|
||||||
|
tab_handlers[notebook.index("current")]()
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
# 绑定事件
|
||||||
|
root.bind('<space>', on_space_pressed)
|
||||||
|
notebook.bind("<<NotebookTabChanged>>", on_tab_changed)
|
||||||
|
|
||||||
|
# 运行主循环
|
||||||
|
root.mainloop()
|
||||||
Reference in New Issue
Block a user