
[2026-2-17 21:45] 更新:修复了一点小错误,f.close后面少了一对括号()
[2026-2-20 17:30] 更新:!!!注意!!!本人新发现本文附带的代码存在安全性漏洞,可能导致未响应,修复后的代码请移步本人新一篇文章!
舒尔特方格是一种注意力训练游戏,通常由25个方格组成的方阵构成,训练时将数字1-25随机填入,被测者需按顺序指读并计时完成 。训练过程中,被测者需用手指依次指出数字位置并诵读,测试者通过生成不同排列的表格进行计时记录。
作者觉得25个不够刺激,增加到36个。
本人制作的舒尔特方格游戏,相较同类轻量级桌面软件,有以下优势:
- 界面现代,美观大方,深色配色,更护眼
- 可实时显示时间
- 可以记录历史记录
- 有按键颜色反馈(正确闪绿色,错误闪红色)
- 全部使用 Python 内置模块完成,无需第三方库
注意!不是把代码复制下去就能运行!需要一个图标文件。win10win11系统里应该都会有这个图标,复制过来放同一个路径下。实在找不到,就把下面这行删掉!
self.root.iconbitmap(PATH + '\\Threat.contrast-black.ico')
全部代码如下:
import os
import random
import threading
import time
from tkinter import *
from tkinter.messagebox import *
from datetime import datetime
import tkinter.ttk
PATH = os.path.dirname(os.path.realpath(__file__)) #编写端
#PATH = os.getcwd() #应用端
class Container:
def __init__(self, app, iid, num, x, y):
self.app = app
self.iid = iid
self.num = num
self.x = x
self.y = y
self.button1 = Button(master = self.app,
font = ('consolas', 16),
borderwidth = 0,
bg = '#2F3133',
fg = 'white'
)
def config(self):
self.button1.config(text = f'{self.num}')
def adapt(self):
self.button1.place(x = self.x, y = self.y, width = 32, height = 32)
class Timer:
def __init__(self, tLabel):
self.running = False
self.tLabel = tLabel
def get_time(self):
start_time = datetime.now()
while True and self.running:
current_time = datetime.now()
time_left = current_time - start_time
m_seconds_left = time_left.microseconds
seconds_left = time_left.seconds
self.tLabel.config(text = f'{int(seconds_left)}.{str(m_seconds_left)[:3]}s ')
time.sleep(0.005)
def stop(self):
self.running = False
try:
self.th.join()
except:
pass
def begin(self):
self.running = True
self.th = threading.Thread(target = self.get_time, name = 'thread_time')
self.th.setDaemon(True)
self.th.start()
class ScrolledText(Text):
def __init__(self, master = None, **kw):
self.frame = Frame(master)
self.vbar = Scrollbar(self.frame)
#self.vbar.pack(side = RIGHT, fill = Y)
kw.update({'yscrollcommand': self.vbar.set})
Text.__init__(self, self.frame, borderwidth = 0, bg = '#222222', fg = 'lime', font = ('consolas', 10), **kw)
self.config(selectbackground = '#222222', selectforeground = 'lime')
self.pack(side = LEFT, fill = BOTH, expand = True)
self.vbar['command'] = self.yview
text_meths = vars(Text).keys()
methods = vars(Pack).keys() | vars(Grid).keys() | vars(Place).keys()
methods = methods.difference(text_meths)
for m in methods:
if m[0] != '_' and m != 'config' and m != 'configure':
setattr(self, m, getattr(self.frame, m))
def __str__(self):
return str(self.frame)
def stop(self):
self['state'] = 'disabled'
class GAME:
"Main thread of FOCUSER."
def __init__(self, state = 1, num_now=1):
"""
The main window of the software.
state = 1 : not started or succeeded.
state = 2 : gaming.
num_now : the number should be clicked.
"""
self.root = Tk()
self.num_now = num_now
self.state = state
self.conts = ["List of Container"]
def config_win(self):
self.root.title('Focus!')
self.root.geometry('248x400')
self.root.resizable(False, False)
self.root.iconbitmap(PATH + '\\Threat.contrast-black.ico')
self.root.config(bg = '#181818')
self.time_label = Label(self.root, text = '0.00s ',
justify = 'right',
anchor = 'e',
fg = 'white',
bg = '#183338',
font = ('consolas', 10)
)
self.reset_but = Button(self.root,
relief = GROOVE,
borderwidth = 0,
text = '开始 / 重新开始',
font = ('微软雅黑', 11),
bg = '#0078D4',
fg = 'white',
activebackground = '#6CACCE',
activeforeground = 'white',
command = None
)
self.timer = Timer(self.time_label)
self.his_cont = ScrolledText(self.root)
def get_history(self):
f = open(PATH + "\\FOCUSER_HISTORY.fcs", 'a', encoding = 'UTF-8')
f.close()
with open(PATH + "\\FOCUSER_HISTORY.fcs", 'r', encoding = 'UTF-8') as ff:
hishis = ff.readlines()
ff.close()
self.his_cont['state'] = 'normal'
for e in hishis:
self.his_cont.insert(INSERT, e)
self.his_cont['state'] = 'disabled'
def save_history(self, ths):
f = open(PATH + "\\FOCUSER_HISTORY.fcs", 'a', encoding = 'UTF-8')
f.write(ths)
f.close()
def judge(self, given):
def bibao():
if given == self.num_now:
self.num_now += 1
for i in range(1, 37):
if self.conts[i].num == self.num_now:
self.conts[i].button1.config(activebackground = '#276235', activeforeground = 'white')
#elif self.conts[i].num == self.num_now:
else:
self.conts[i].button1.config(activebackground = '#8D1D2C', activeforeground = 'white')
if self.num_now == 37:
self.timer.stop()
msg = '[' + str(datetime.now())[:19] + ']\t' + self.time_label.cget('text') + '\n'
self.his_cont['state'] = 'normal'
self.his_cont.insert(INSERT, msg)
self.his_cont['state'] = 'disabled'
self.save_history(msg)
else:
pass
return bibao
def new_game(self):
self.state = 2
self.timer.stop()
self.tns = [i for i in range(1, 37)]
random.shuffle(self.tns)
self.tns = [0] + self.tns
for i in range(1, 37):
self.conts[i].num = self.tns[i]
self.conts[i].config()
self.conts[i].button1.config(command = self.judge(self.conts[i].num))
self.num_now = 0
st1 = self.judge(0)
st1()
self.timer.begin()
def pack_buts(self):
"Add Buttons to Main window."
for i in range(1, 37):
X = 8 + ((i-1) // 6) * 40
Y = 8 + ((i-1) % 6) * 40
self.conts.append(Container(self.root, iid = i, num = 0, x = X, y = Y))
self.conts[-1].adapt()
self.time_label.place(x = 240, y = 248, height = 30, width = 72, anchor = 'ne')
self.reset_but.config(command = self.new_game)
self.reset_but.place(x = 8, y = 248, width = 152, height = 30)
self.his_cont.place(x = 8, y = 286, width = 232, height = 90)
self.his_cont['state'] = 'disabled'
def run(self):
self.root.mainloop()
a = GAME()
a.config_win()
a.pack_buts()
a.get_history()
a.run()
完毕。
406

被折叠的 条评论
为什么被折叠?



