Skip to content

Instantly share code, notes, and snippets.

@NoNormalCreeper
Created September 17, 2025 05:59
Show Gist options
  • Select an option

  • Save NoNormalCreeper/46ab1ef93dceae1286f398848f999792 to your computer and use it in GitHub Desktop.

Select an option

Save NoNormalCreeper/46ab1ef93dceae1286f398848f999792 to your computer and use it in GitHub Desktop.
本次班级互评的问卷星自动打分脚本
import time
import sys
import threading
try:
import keyboard # for hotkey / key events
except ImportError:
keyboard = None
try:
import pyautogui # for typing
except ImportError:
pyautogui = None
ANSWERS = ["22", "22", "18", "13", "13"] # 默认分数
ANSWERS_2 = ["25", "25", "20", "15", "15"]
EXCELLENT_STUDENTS = [2, 3, 4, 11, 25, 26, 30] # 打高分的学生编号,以问卷中的为准
GROUP_COUNT = 29
START_KEY = "p" # 用户按下 P 键开始
ABORT_KEY = "esc" # 用户按下 ESC 立即中止
COUNTDOWN_SECONDS = 5
INTER_ANSWER_DELAY = 0.1 # 每个答案之间等待
TYPE_CHAR_INTERVAL = 0.02 # 输入每个答案时字符间隔(更像人工)
# 已更新:逐个答案后立即 Tab
action_lock = threading.Lock()
should_abort = False
def ensure_libs():
missing = []
if keyboard is None:
missing.append("keyboard")
if pyautogui is None:
missing.append("pyautogui")
if missing:
print("缺少依赖,请先安装: pip install " + " ".join(missing))
sys.exit(1)
def abort_listener():
global should_abort
if keyboard is None:
return
while True:
if keyboard.is_pressed(ABORT_KEY):
with action_lock:
should_abort = True
break
time.sleep(0.05)
def log(msg: str):
ts = time.strftime('%H:%M:%S')
print(f"[{ts}] {msg}")
def wait_for_start_key():
log(f"等待你按下 '{START_KEY.upper()}' 键以启动 (ESC 终止)...")
while True:
if keyboard.is_pressed(ABORT_KEY):
return False
if keyboard.is_pressed(START_KEY):
return True
time.sleep(0.05)
def countdown(seconds: int):
for remaining in range(seconds, 0, -1):
with action_lock:
if should_abort:
return False
log(f"倒计时 {remaining} 秒...")
time.sleep(1)
return True
def type_answer(text: str, group_idx: int, idx_in_group: int):
log(f"输入 第{group_idx}组 第{idx_in_group}空 -> {text}")
pyautogui.typewrite(text, interval=TYPE_CHAR_INTERVAL)
def main():
ensure_libs()
# 后台监听 ESC
t = threading.Thread(target=abort_listener, daemon=True)
t.start()
if not wait_for_start_key():
log("已取消。")
return
log(f"将在 {COUNTDOWN_SECONDS} 秒后开始,请切换到浏览器并将光标放在第一题第一个输入框内。")
if not countdown(COUNTDOWN_SECONDS):
log("已取消。")
return
log("开始自动填写... (ESC 可随时终止)")
for group_idx in range(1, GROUP_COUNT + 1):
with action_lock:
if should_abort:
break
log(f"开始填写第 {group_idx}/{GROUP_COUNT} 组")
if group_idx + 1 in EXCELLENT_STUDENTS:
current_answers = ANSWERS_2
else:
current_answers = ANSWERS
for i, ans in enumerate(current_answers, start=1):
with action_lock:
if should_abort:
break
type_answer(ans, group_idx, i)
time.sleep(INTER_ANSWER_DELAY)
keyboard.send('tab')
if should_abort:
break
# keyboard.send('tab') # 额外一次跳到下一题第一空
log(f"第 {group_idx} 组完成,跳到下一题。")
time.sleep(0.25)
if should_abort:
log("用户终止。")
else:
log("全部填写完成(未提交)。")
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
log("用户通过 Ctrl+C 中断。")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment