基于 Python Tkinter 制作的舒尔特方格游戏

[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()

完毕。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值