Python可视化训练
一 实验目的
l 使学生综合运用图形用户界面设计的概念;
l 使学生熟悉使用中间面板,组成层次复杂的GUI界面;
l 使学生掌握Python图形绘制和图像处理步骤与方法;
l 使学生掌握Python可视化处理的步骤、方法与编程;
二 实验环境及实验准备
l 所需硬件环境为微机;
l 所需软件环境为Python 3.X等;
l 掌握Python下界面容器与基本组件的基本知识与应用;
l 掌握Python下事件处理模型;
l 掌握Python下图形绘制的方法;
三 实验内容
(一)、设计实现电子算盘,并完成测试
【题目描述】
给小朋友设计一个电子算盘。要求绘制电子算盘界面,设计并实现打珠算过程(界面参考如下图示)。
界面右侧要求以图形绘制的方式绘制自画像,注意不能是图像文件显示的形式。
【源代码程序】
import tkinter as tk
from tkinter import Canvas
class ElectronicAbacusPlusSelfPortrait:
def __init__(self, root):
self.root = root
self.root.title("Children's Electronic Abacus and Self-portrait")
self.canvas = tk.Canvas(root, width=800, height=500) # 修改了画布大小以便更好地展示内容
self.canvas.pack()
self.initWindow() # 绘制棋盘
self.draw_self_portrait() # 绘制自画像
self.bind_bead_events() # 绑定棋子事件
def initWindow(self):
# 绘制算盘边框
self.canvas.create_rectangle(25, 40, 450, 400, width=3)
# 生成串算珠的线
x0, y0, x1, y1 = 0, 0, 0, 0
for i in range(5):
self.canvas.create_line(70 + x0, 40 + y0, 70 + x1, 400 + y1, width=3)
x0 += 80
x1 += 80
# 生成上下珠的分割线
self.canvas.create_line(25, 100, 450, 100, width=3)
# 生成5个上珠
self.top_oval = [None] * 5
x0, y0, x1, y1 = 0, 0, 0, 0
for i in range(5):
self.top_oval[i] = self.canvas.create_oval(40 + x0, 60 + y0, 100 + x1, 90 + y1, fill='orange', tags=f"top{i}")
x0 += 80
x1 += 80
# 生成4*5个下珠
self.below_oval = [[None] * 5 for _ in range(4)]
self.chushi = [[None] * 5 for _ in range(4)]
x0, y0, x1, y1 = 0, 0, 0, 0
for i in range(4):
for j in range(5):
self.below_oval[i][j] = self.canvas.create_oval(40 + x0, 160 + y0, 100 + x1, 190 + y1, fill='yellow', tags=f"below{i}{j}")
self.chushi[i][j] = self.canvas.coords(self.below_oval[i][j])
x0 += 80
x1 += 80
x0 = 0
x1 = 0
y0 += 60
y1 += 60
def draw_self_portrait(self):
# 画布右侧绘制自画像,坐标经过调整以适应右侧区域
# Background
self.canvas.create_rectangle(550, 0, 800, 500, fill='White Smoke', outline='purple')
# Head
self.canvas.create_oval(600, 50, 750, 200, fill='Peach Puff')
# Hair
self.canvas.create_rectangle(600, 50, 750, 90, fill='yellow') # Top hair
self.canvas.create_rectangle(580, 90, 610, 130, fill='yellow') # Left side hair
self.canvas.create_rectangle(740, 90, 770, 130, fill='yellow') # Right side hair
# Eyes
self.canvas.create_oval(630, 100, 650, 120, fill='black') # Left eye
self.canvas.create_oval(700, 100, 720, 120, fill='black') # Right eye
# Mouth
self.canvas.create_line(640, 170, 710, 170, fill='red') # Mouth as a simple red line
# Necklace
self.canvas.create_line(620, 210, 730, 210, fill='black') # Necklace string
self.canvas.create_oval(674, 207, 677, 210, fill='Light Pink') # Small pendant
# Shoulders
self.canvas.create_line(590, 210, 620, 230, fill='dark grey') # Left shoulder
self.canvas.create_line(730, 210, 760, 230, fill='dark grey') # Right shoulder
# Attire with a floral pattern
self.canvas.create_rectangle(610, 203, 740, 235, fill='Light Pink') # Shirt
def get_empty(self):
empty = [[0 for j in range(5)] for i in range(4)]
for i in range(4):
for j in range(5):
if self.canvas.coords(self.below_oval[i][j]) != self.chushi[i][j]:
empty[i][j] = 1
return empty
def bind_bead_events(self):
def handler_adaptor(handler, fun, row, col):
"""事件处理函数的适配器,相当于中介,可以帮助tag_bind函数传递参数"""
return lambda event, handler=handler, fun=fun, col=col, row=row: handler(event=event, fun=fun, row=row, col=col)
def handler_adaptor2(handler2, fun, row):
"""事件处理函数的适配器,相当于中介,可以帮助tag_bind函数传递参数"""
return lambda event, handler2=handler2, fun=fun, row=row: handler2(event=event, fun=fun, row=row)
def handler(event, fun, row, col):
"""下珠上划"""
content = fun # 这个就是被点击的算珠id
empty = self.get_empty()
if row == 0:
if float(self.canvas.coords(content)[1]) - 40 >= 100:
self.canvas.move(content, 0, -40)
else:
if empty[row - 1][col] == 1:
if float(self.canvas.coords(content)[1]) - 40 >= 110 + 10 * (row + 1):
self.canvas.move(content, 0, -40)
def handler2(event, fun, row):
"""上珠上划"""
content = fun # 这个就是被点击的算珠id
if float(self.canvas.coords(content)[1]) - 20 >= 40:
self.canvas.move(content, 0, -20)
def handler3(event, fun, row, col):
"""下珠下划"""
content = fun # 这个就是被点击的算珠id
empty = self.get_empty()
if row == 3:
if float(self.canvas.coords(content)[1]) + 40 <= 350:
self.canvas.move(content, 0, 40)
else:
if empty[row][col] == 1:
self.canvas.move(content, 0, 40)
def handler4(event, fun, row):
"""上珠下划"""
content = fun # 这个就是被点击的算珠id
if float(self.canvas.coords(content)[1]) + 20 <= 60:
self.canvas.move(content, 0, 20)
for i in range(5):
self.canvas.tag_bind(self.top_oval[i], "<Button-1>", handler_adaptor2(handler2, fun=self.top_oval[i], row=i))
self.canvas.tag_bind(self.top_oval[i], "<Button-3>", handler_adaptor2(handler4, fun=self.top_oval[i], row=i))
for i in range(4):
for j in range(5):
self.canvas.tag_bind(self.below_oval[i][j], "<Button-1>", handler_adaptor(handler, fun=self.below_oval[i][j], row=i, col=j))
self.canvas.tag_bind(self.below_oval[i][j], "<Button-3>", handler_adaptor(handler3, fun=self.below_oval[i][j], row=i, col=j))
# 创建主窗口
root = tk.Tk()
abacus_and_self_portrait = ElectronicAbacusPlusSelfPortrait(root)
# 运行主循环,显示窗口
root.mainloop()
【运行测试】
(二)、以(一)中的电子算盘为基础,设计并实现珠算测试器,并完成测试。
【题目描述】
给小朋友设计一个珠算测试器,要求能够完成珠算加减法的测试。具体的要求功能如下:
(1) 用户启动测试,输入用户名后系统随机生成特定数目的加减法测试题;
(2) 要求测试使用表盘式或数字时秒表进行界面计时显示(参考如上图示);
(3) 对于每道测试题目,要求用户使用电子算盘完成珠算过程,当按下确认键时,将珠算结果与正确答案比对,并在界面上显示总题数、已答题数和已做对题数;
(4) 当测试完成,界面显示本次测试情况(包括用户名、测试题目及答题明细、对错情况、测试用时和测试成绩)
【源代码程序】
import tkinter as tk
from tkinter import Canvas, simpledialog, messagebox
import random
import time
class AbacusTestApp:
def __init__(self, root):
self.root = root
self.root.title("Children's Abacus Test")
self.canvas = tk.Canvas(root, width=800, height=500)
self.canvas.pack(side=tk.LEFT)
self.initWindow()
self.questions = []
self.current_question_index = 0
self.correct_answers = 0
self.start_time = None
self.username = ""
self.info_frame = tk.Frame(root)
self.info_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
self.question_label = tk.Label(self.info_frame, text="Question: ", font=("Arial", 14))
self.question_label.pack()
self.timer_label = tk.Label(self.info_frame, text="Time: 0", font=("Arial", 14))
self.timer_label.pack()
self.result_label = tk.Label(self.info_frame, text="", font=("Arial", 14))
self.result_label.pack()
self.start_button = tk.Button(self.info_frame, text="开始测试", command=self.start_test)
self.start_button.pack(pady=10)
self.submit_button = tk.Button(self.info_frame, text="提交答案", command=self.submit_answer)
self.submit_button.pack(pady=10)
self.bind_bead_events()
def initWindow(self):
# 绘制算盘边框
self.canvas.create_rectangle(25, 40, 450, 400, width=3)
# 生成串算珠的线
x0, y0, x1, y1 = 0, 0, 0, 0
for i in range(5):
self.canvas.create_line(70 + x0, 40 + y0, 70 + x1, 400 + y1, width=3)
x0 += 80
x1 += 80
# 生成上下珠的分割线
self.canvas.create_line(25, 100, 450, 100, width=3)
# 生成5个上珠
self.top_oval = [None] * 5
x0, y0, x1, y1 = 0, 0, 0, 0
for i in range(5):
self.top_oval[i] = self.canvas.create_oval(40 + x0, 60 + y0, 100 + x1, 90 + y1, fill='orange', tags=f"top{i}")
x0 += 80
x1 += 80
# 生成4*5个下珠
self.below_oval = [[None] * 5 for _ in range(4)]
self.chushi = [[None] * 5 for _ in range(4)]
x0, y0, x1, y1 = 0, 0, 0, 0
for i in range(4):
for j in range(5):
self.below_oval[i][j] = self.canvas.create_oval(40 + x0, 160 + y0, 100 + x1, 190 + y1, fill='yellow', tags=f"below{i}{j}")
self.chushi[i][j] = self.canvas.coords(self.below_oval[i][j])
x0 += 80
x1 += 80
x0 = 0
x1 = 0
y0 += 60
y1 += 60
self.bind_bead_events()
def get_empty(self):
empty = [[0 for j in range(5)] for i in range(4)]
for i in range(4):
for j in range(5):
if self.canvas.coords(self.below_oval[i][j]) != self.chushi[i][j]:
empty[i][j] = 1
return empty
def bind_bead_events(self):
def handler_adaptor(handler, fun, row, col):
return lambda event, handler=handler, fun=fun, col=col, row=row: handler(event=event, fun=fun, row=row, col=col)
def handler_adaptor2(handler2, fun, row):
return lambda event, handler2=handler2, fun=fun, row=row: handler2(event=event, fun=fun, row=row)
def handler(event, fun, row, col):
content = fun
empty = self.get_empty()
if row == 0:
if float(self.canvas.coords(content)[1]) - 40 >= 100:
self.canvas.move(content, 0, -40)
else:
if empty[row - 1][col] == 1:
if float(self.canvas.coords(content)[1]) - 40 >= 110 + 10 * (row + 1):
self.canvas.move(content, 0, -40)
def handler2(event, fun, row):
content = fun
if float(self.canvas.coords(content)[1]) - 20 >= 40:
self.canvas.move(content, 0, -20)
def handler3(event, fun, row, col):
content = fun
empty = self.get_empty()
if row == 3:
if float(self.canvas.coords(content)[1]) + 40 <= 350:
self.canvas.move(content, 0, 40)
else:
if empty[row][col] == 1:
self.canvas.move(content, 0, 40)
def handler4(event, fun, row):
content = fun
if float(self.canvas.coords(content)[1]) + 20 <= 60:
self.canvas.move(content, 0, 20)
for i in range(5):
self.canvas.tag_bind(self.top_oval[i], "<Button-1>", handler_adaptor2(handler2, fun=self.top_oval[i], row=i))
self.canvas.tag_bind(self.top_oval[i], "<Button-3>", handler_adaptor2(handler4, fun=self.top_oval[i], row=i))
for i in range(4):
for j in range(5):
self.canvas.tag_bind(self.below_oval[i][j], "<Button-1>", handler_adaptor(handler, fun=self.below_oval[i][j], row=i, col=j))
self.canvas.tag_bind(self.below_oval[i][j], "<Button-3>", handler_adaptor(handler3, fun=self.below_oval[i][j], row=i, col=j))
def start_test(self):
self.username = simpledialog.askstring("Input", "Enter your username:", parent=self.root)
if not self.username:
return
self.questions = self.generate_questions()
self.current_question_index = 0
self.correct_answers = 0
self.start_time = time.time()
self.update_timer()
self.show_question()
def generate_questions(self):
questions = []
for _ in range(5): # 生成5个测试题
a = random.randint(1, 50)
b = random.randint(1, 50)
operation = random.choice(["+", "-"])
if operation == "+":
answer = a + b
else:
answer = a - b
questions.append((a, operation, b, answer))
return questions
def show_question(self):
if self.current_question_index < len(self.questions):
question = self.questions[self.current_question_index]
self.question_label.config(text=f"Question: {question[0]} {question[1]} {question[2]} = ?")
else:
self.end_test()
def submit_answer(self):
# 在这里获取用户通过算盘的输入
user_answer = simpledialog.askstring("Input", "Enter your answer:", parent=self.root)
if not user_answer.isdigit():
messagebox.showerror("Invalid input", "Please enter a valid number.")
return
user_answer = int(user_answer)
correct_answer = self.questions[self.current_question_index][3]
if user_answer == correct_answer:
self.correct_answers += 1
self.current_question_index += 1
self.show_question()
self.update_result_label()
def calculate_abacus_value(self):
value = 0
for i in range(5):
if float(self.canvas.coords(self.top_oval[i])[1]) < 60:
value += 5 * (10 ** (4 - i))
for j in range(4):
if float(self.canvas.coords(self.below_oval[j][i])[1]) < self.chushi[j][i][1]:
value += (j + 1) * (10 ** (4 - i))
return value
def update_result_label(self):
total_questions = len(self.questions)
answered_questions = self.current_question_index
self.result_label.config(text=f"User: {self.username}\n,Total: {total_questions}, Answered: {answered_questions}, Correct: {self.correct_answers}")
def end_test(self):
total_time = time.time() - self.start_time
result_message = f"User: {self.username}\n"
result_message += f"Total Questions: {len(self.questions)}\n"
result_message += f"Correct Answers: {self.correct_answers}\n"
result_message += f"Time Taken: {total_time:.2f} seconds\n"
def update_timer(self):
if self.current_question_index < len(self.questions):
elapsed_time = time.time() - self.start_time
self.timer_label.config(text=f"Time: {int(elapsed_time)}")
self.root.after(1000, self.update_timer)
if __name__ == "__main__":
root = tk.Tk()
app = AbacusTestApp(root)
#submit_button = tk.Button(root, text="提交当前答案", command=app.submit_answer)
submit_button.pack()
root.mainloop()
【运行测试】
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现