Pygame打造趣味计算器:基础功能与浪漫彩蛋的完美结合

项目亮点

这个Pygame计算器程序不仅实现了基本的计算功能,还通过精心设计的浪漫彩蛋增添了趣味性。项目展示了如何利用Pygame创建交互式图形界面,处理用户输入,以及实现一些有趣的附加功能。

在Python的图形界面开发领域,Pygame是一个强大而灵活的库,特别适合开发交互式应用程序和小游戏。本文将带你了解如何使用Pygame创建一个功能完整的计算器程序,不仅具备基本的数学运算能力,还隐藏着一个充满浪漫气息的彩蛋功能。

一、程序整体设计与功能概述

这个计算器程序采用了简洁明了的设计思路,主要实现了以下功能:

程序的整体结构采用了模块化设计,将界面绘制、事件处理、计算逻辑等功能分开实现,使得代码结构清晰,易于维护和扩展。


二、初始化与基本设置

1. 环境初始化与资源准备

程序开始部分进行了Pygame的初始化,并定义了所需的颜色、屏幕尺寸等基本参数:

import pygame
import sys
import time

# 初始化Pygame
pygame.init()

# 颜色定义
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (200, 200, 200)
DARK_GRAY = (100, 100, 100)
BLUE = (0, 100, 255)
PINK = (255, 182, 193)  # 彩蛋背景色
RED = (255, 0, 0)       # 返回按钮颜色

# 屏幕设置(更大的尺寸)
WIDTH, HEIGHT = 480, 800  # 增大到480×800
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("计算器")

这里定义了多种颜色用于界面元素的绘制,并将屏幕尺寸设置为480×800像素,使得计算器界面更加宽敞,按钮尺寸更大,提升用户体验。

2. 字体处理与符号显示

为了解决数学符号(尤其是乘号)的显示问题,程序专门设计了字体处理函数:

# 字体处理函数(解决符号显示问题)
def get_proper_font(size):
    """获取支持数学符号的字体"""
    font_options = ['arial', 'segoe ui symbol', 'symbola', 'dejavusans']
    for font_name in font_options:
        try:
            font = pygame.font.SysFont(font_name, size)
            # 测试乘号渲染
            test_surf = font.render("×", True, BLACK)
            if test_surf.get_width() > 5:  # 确保能渲染符号
                return font
        except:
            continue
    return pygame.font.SysFont(None, size)  # 回退到默认字体

# 初始化字体(调整后的大小)
font_large = get_proper_font(44)  # 主显示屏字体
font_small = get_proper_font(34)  # 按钮字体
font_easter_egg = get_proper_font(38)  # 彩蛋字体

这个函数会尝试多种字体,确保能够正确渲染乘号等特殊符号。如果所有指定字体都不支持,才会回退到系统默认字体。

设计思考

字体兼容性是跨平台应用开发中常见的问题。这里采用的多字体回退机制确保了程序在不同操作系统上都能正常工作,体现了良好的兼容性设计。

对于可能仍然无法正确显示的情况,程序还提供了手动绘制乘号的函数:

# 手动绘制乘号(防止字体不支持)
def draw_custom_multiply(x, y, size=35, color=BLACK, thickness=4):
    """绘制自定义乘号(替代字体渲染)"""
    pygame.draw.line(screen, color, (x, y), (x+size, y+size), thickness)
    pygame.draw.line(screen, color, (x+size, y), (x, y+size), thickness)

三、界面设计与按钮布局

1. 计算器状态管理

在设计界面之前,首先需要定义计算器的各种状态变量:

# 计算器状态
current_input = "0"
previous_input = ""
operation = None
reset_input = False
show_easter_egg = False
easter_egg_start_time = 0

这些变量分别记录了当前输入、之前的输入、操作符、是否重置输入、是否显示彩蛋以及彩蛋开始时间等状态信息。

2. 按钮布局与绘制

计算器的按钮布局采用了网格状设计,代码中详细定义了每个按钮的位置、尺寸和颜色:

# 按钮布局(更大的按钮和间距)
button_size = 100  # 增大到100像素
margin = 15        # 边距增大到15像素
buttons = [
    {"label": "C", "rect": pygame.Rect(margin, 220, button_size, button_size), "color": GRAY},
    {"label": "+/-", "rect": pygame.Rect(margin*2 + button_size, 220, button_size, button_size), "color": GRAY},
    {"label": "%", "rect": pygame.Rect(margin*3 + button_size*2, 220, button_size, button_size), "color": GRAY},
    {"label": "÷", "rect": pygame.Rect(margin*4 + button_size*3, 220, button_size, button_size), "color": DARK_GRAY},
    
    {"label": "7", "rect": pygame.Rect(margin, 220 + button_size + margin, button_size, button_size), "color": WHITE},
    {"label": "8", "rect": pygame.Rect(margin*2 + button_size, 220 + button_size + margin, button_size, button_size), "color": WHITE},
    {"label": "9", "rect": pygame.Rect(margin*3 + button_size*2, 220 + button_size + margin, button_size, button_size), "color": WHITE},
    {"label": "×", "rect": pygame.Rect(margin*4 + button_size*3, 220 + button_size + margin, button_size, button_size), "color": DARK_GRAY},
    
    {"label": "4", "rect": pygame.Rect(margin, 220 + (button_size + margin)*2, button_size, button_size), "color": WHITE},
    {"label": "5", "rect": pygame.Rect(margin*2 + button_size, 220 + (button_size + margin)*2, button_size, button_size), "color": WHITE},
    {"label": "6", "rect": pygame.Rect(margin*3 + button_size*2, 220 + (button_size + margin)*2, button_size, button_size), "color": WHITE},
    {"label": "-", "rect": pygame.Rect(margin*4 + button_size*3, 220 + (button_size + margin)*2, button_size, button_size), "color": DARK_GRAY},
    
    {"label": "1", "rect": pygame.Rect(margin, 220 + (button_size + margin)*3, button_size, button_size), "color": WHITE},
    {"label": "2", "rect": pygame.Rect(margin*2 + button_size, 220 + (button_size + margin)*3, button_size, button_size), "color": WHITE},
    {"label": "3", "rect": pygame.Rect(margin*3 + button_size*2, 220 + (button_size + margin)*3, button_size, button_size), "color": WHITE},
    {"label": "+", "rect": pygame.Rect(margin*4 + button_size*3, 220 + (button_size + margin)*3, button_size, button_size), "color": DARK_GRAY},
    
    {"label": "0", "rect": pygame.Rect(margin, 220 + (button_size + margin)*4, button_size*2 + margin, button_size), "color": WHITE},
    {"label": ".", "rect": pygame.Rect(margin*3 + button_size*2, 220 + (button_size + margin)*4, button_size, button_size), "color": WHITE},
    {"label": "=", "rect": pygame.Rect(margin*4 + button_size*3, 220 + (button_size + margin)*4, button_size, button_size), "color": BLUE},
]

这里将按钮尺寸设置为100像素,并留有15像素的边距,使得按钮更加宽敞,便于点击。特别值得注意的是0按钮的宽度是其他按钮的两倍,这符合传统计算器的设计习惯。

按钮的绘制函数实现了文字的完美居中显示:

def draw_button(button):
    """绘制计算器按钮(完美居中版本)"""
    pygame.draw.rect(screen, button["color"], button["rect"])
    pygame.draw.rect(screen, BLACK, button["rect"], 2)
    
    label = button["label"]
    if label == "×":  # 特殊处理乘号
        draw_custom_multiply(
            button["rect"].x + (button["rect"].width - 35) // 2,
            button["rect"].y + (button["rect"].height - 35) // 2,
            35, BLACK, 4
        )
    else:
        text = font_small.render(label, True, BLACK)
        # 精确计算文字位置(完全居中)
        text_x = button["rect"].x + (button["rect"].width - text.get_width()) // 2
        text_y = button["rect"].y + (button["rect"].height - text.get_height()) // 2
        screen.blit(text, (text_x, text_y))

四、核心计算逻辑实现

1. 计算功能函数

计算器的核心计算逻辑由perform_calculation函数实现:

def perform_calculation():
    """执行计算逻辑"""
    global current_input, previous_input, operation, show_easter_egg, easter_egg_start_time
    
    if operation and previous_input:
        try:
            num1 = float(previous_input)
            num2 = float(current_input)
            
            # 彩蛋触发条件:1314×520
            if abs(num1 - 1314) < 0.001 and abs(num2 - 520) < 0.001 and operation == "×":
                show_easter_egg = True
                easter_egg_start_time = time.time()
                return
            
            # 正常计算
            if operation == "+":
                result = num1 + num2
            elif operation == "-":
                result = num1 - num2
            elif operation == "×":
                result = num1 * num2
            elif operation == "÷":
                result = num1 / num2 if num2 != 0 else "错误"
            elif operation == "%":
                result = num1 * (num2 / 100)
            
            current_input = str(result) if result != "错误" else result
            previous_input = ""
            operation = None
        except:
            current_input = "错误"
            previous_input = ""
            operation = None

这个函数首先检查是否存在有效的操作符和输入值,然后将输入转换为浮点数进行计算。特别值得注意的是彩蛋触发条件的判断:当检测到计算1314×520时,会设置show_easter_egg标志为True,从而触发彩蛋界面的显示。

彩蛋设计

1314和520在中文网络文化中分别谐音"一生一世"和"我爱你",这个彩蛋设计巧妙地将数学计算与浪漫表达结合在一起,增加了程序的趣味性和情感价值。

2. 按钮点击事件处理

handle_button_click函数负责处理所有按钮的点击事件:

def handle_button_click(label):
    """处理按钮点击事件"""
    global current_input, previous_input, operation, reset_input, show_easter_egg
    
    if show_easter_egg and label == "返回":
        show_easter_egg = False
        current_input = "0"
        previous_input = ""
        operation = None
        return
    
    if show_easter_egg:
        return
    
    if label in "0123456789":
        if current_input == "0" or reset_input:
            current_input = label
            reset_input = False
        else:
            current_input += label
    elif label == ".":
        if "." not in current_input:
            current_input += "."
    elif label == "+/-":
        if current_input.startswith("-"):
            current_input = current_input[1:]
        else:
            current_input = "-" + current_input
    elif label == "C":
        current_input = "0"
        previous_input = ""
        operation = None
    elif label in "+-×÷%":
        if operation and previous_input and not reset_input:
            perform_calculation()
        previous_input = current_input
        operation = label
        reset_input = True
    elif label == "=":
        if operation and previous_input:
            perform_calculation()
            reset_input = True

该函数根据不同的按钮标签执行相应的操作,包括数字输入、小数点处理、正负转换、清除操作、运算符选择和等号计算等。


五、浪漫彩蛋功能实现

1. 彩蛋界面设计

当触发彩蛋条件后,程序会显示一个特别的浪漫界面:

# 彩蛋返回按钮(更大更明显)
back_button = {"label": "返回", "rect": pygame.Rect(WIDTH//2 - 60, HEIGHT - 120, 120, 70), "color": RED}

彩蛋界面的绘制在主循环中单独处理:

# 显示彩蛋
if show_easter_egg:
    # 粉色背景(覆盖整个屏幕)
    pygame.draw.rect(screen, PINK, (0, 0, WIDTH, HEIGHT))
    
    # 计算中心位置
    center_x = WIDTH // 2
    center_y = HEIGHT // 2
    
    # 渲染计算式(完美对齐)
    text1 = font_easter_egg.render("1314", True, BLACK)
    text2 = font_easter_egg.render("520 = 683280", True, BLACK)
    
    # 计算整体宽度
    total_width = text1.get_width() + 40 + text2.get_width()  # 40是乘号宽度
    
    # 从中心点开始向左偏移一半
    start_x = center_x - total_width // 2
    
    # 绘制1314
    screen.blit(text1, (start_x, center_y - 70))
    
    # 绘制乘号(精确居中)
    draw_custom_multiply(start_x + text1.get_width() + 10, center_y - 55, 40, BLACK, 4)
    
    # 绘制520 = 683280
    screen.blit(text2, (start_x + text1.get_width() + 50, center_y - 70))
    
    # 绘制浪漫文字(完全居中)
    text3 = font_easter_egg.render("Love You", True, BLACK)
    text3_rect = text3.get_rect(center=(center_x, center_y))
    screen.blit(text3, text3_rect)
    
    text4 = font_easter_egg.render("Forever", True, BLACK)
    text4_rect = text4.get_rect(center=(center_x, center_y + 50))
    screen.blit(text4, text4_rect)
    
    # 绘制返回按钮(文字居中)
    pygame.draw.rect(screen, back_button["color"], back_button["rect"])
    pygame.draw.rect(screen, BLACK, back_button["rect"], 3)
    back_text = font_small.render(back_button["label"], True, WHITE)
    text_x = back_button["rect"].x + (back_button["rect"].width - back_text.get_width()) // 2
    text_y = back_button["rect"].y + (back_button["rect"].height - back_text.get_height()) // 2
    screen.blit(back_text, (text_x, text_y))
    
    pygame.display.flip()
    continue

这个界面以粉色为背景,中央显示"1314×520=683280"的计算式,下方还有"Love You"和"Forever"的浪漫文字,营造出温馨浪漫的氛围。

2. 彩蛋超时处理

为了不影响计算器的正常使用,彩蛋界面设置了5秒的自动返回机制:

# 彩蛋超时检测(5秒后自动返回)
if show_easter_egg and time.time() - easter_egg_start_time > 5:
    show_easter_egg = False
    current_input = "0"
    previous_input = ""
    operation = None

六、主循环与程序运行

程序的主循环负责处理事件、更新界面和响应用户操作:

# 主循环
running = True
while running:
    screen.fill(BLACK)
    
    # 彩蛋超时检测(5秒后自动返回)
    if show_easter_egg and time.time() - easter_egg_start_time > 5:
        show_easter_egg = False
        current_input = "0"
        previous_input = ""
        operation = None
    
    # 显示彩蛋(略,前面已介绍)
    
    # 显示当前输入和操作历史
    # 显示当前输入(右对齐优化)
    input_text = current_input if len(current_input) <= 12 else current_input[:12]
    text_surface = font_large.render(input_text, True, WHITE)
    
    # 计算文字位置(右侧留20像素边距)
    text_x = WIDTH - text_surface.get_width() - 20
    text_y = 160  # 从顶部向下160像素
    
    # 绘制文字背景(避免残影)
    pygame.draw.rect(screen, BLACK, (text_x, text_y - text_surface.get_height(), 
                                    text_surface.get_width(), text_surface.get_height()))
    
    screen.blit(text_surface, (text_x, text_y))
    
    # 显示操作历史(右对齐优化)
    if previous_input:
        history_text = f"{previous_input} {operation}"
        history_surface = font_small.render(history_text, True, GRAY)
        
        # 计算历史记录位置(当前输入上方)
        history_x = WIDTH - history_surface.get_width() - 20
        history_y = text_y - history_surface.get_height() - 10
        
        pygame.draw.rect(screen, BLACK, (history_x, history_y - history_surface.get_height(), 
                                        history_surface.get_width(), history_surface.get_height()))
        screen.blit(history_surface, (history_x, history_y))
    
    # 绘制所有按钮
    for button in buttons:
        draw_button(button)
    
    # 事件处理
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEBUTTONDOWN:
            mouse_pos = pygame.mouse.get_pos()
            
            if show_easter_egg:
                if back_button["rect"].collidepoint(mouse_pos):
                    handle_button_click("返回")
            else:
                for button in buttons:
                    if button["rect"].collidepoint(mouse_pos):
                        handle_button_click(button["label"])
    
    pygame.display.flip()

pygame.quit()
sys.exit()

主循环中,程序首先处理彩蛋超时情况,然后根据是否显示彩蛋决定绘制普通计算器界面还是彩蛋界面,最后处理用户的鼠标点击事件。


七、程序特点与拓展方向

1. 程序主要特点

2. 可能的拓展方向

这个Pygame计算器程序不仅实现了基本的计算功能,还通过浪漫彩蛋的设计增添了趣味性,是一个很好的Pygame应用实例。通过这个项目,我们可以学习如何利用Pygame创建交互式图形界面,处理用户输入,以及实现一些有趣的附加功能。