Python 游戏开发与文件处理:PyGame + Turtle + openpyxl + python-docx + PyPDF2
Python 游戏开发与文件处理:PyGame + Turtle + openpyxl + python-docx + PyPDF2
专栏:Python 常用工具库实战教程 | 第六篇
适合人群:Python 趣味编程爱好者、办公自动化需求者
目录
- 前言:Python 不只是数据和 Web
- 第一章:PyGame —— 2D 游戏开发
- 第二章:Turtle —— 入门级绘图
- 第三章:openpyxl —— Excel 处理
- 第四章:python-docx —— Word 文档处理
- 第五章:PyPDF2 —— PDF 处理
- 总结
前言
Python 的应用场景远不止数据科学和 Web 开发。本章将介绍 Python 在游戏开发和办公自动化领域的两大类工具:

| 工具 | 定位 | 学习难度 | 典型用途 |
|---|---|---|---|
| PyGame | 2D 游戏引擎 | ⭐⭐⭐ | 小游戏开发 |
| Turtle | 教学绘图 | ⭐ | 编程入门教学 |
| openpyxl | Excel 处理 | ⭐⭐ | 报表生成、数据分析 |
| python-docx | Word 处理 | ⭐⭐ | 合同生成、报告编写 |
| PyPDF2 | PDF 处理 | ⭐⭐ | PDF 合并、提取 |

第一章:PyGame —— 2D 游戏开发
1.1 PyGame 是什么
PyGame 是基于 SDL(Simple DirectMedia Layer)库的 Python 游戏开发框架。它可以创建 2D 游戏、交互式应用和多媒体程序。
pip install pygame
PyGame 的核心能力:
- 创建和管理游戏窗口
- 处理键盘、鼠标等输入事件
- 绘制图形、文字和精灵(Sprite)
- 播放声音和背景音乐
- 碰撞检测
- 精灵动画
1.2 PyGame 基本架构
每个 PyGame 程序都遵循相同的基本架构:
import pygame
import sys
# 1. 初始化
pygame.init()
# 2. 创建窗口
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption('我的第一个游戏')
# 3. 游戏主循环
clock = pygame.time.Clock()
running = True
while running:
# 3.1 处理事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
# 3.2 更新游戏状态
# (移动、碰撞、得分等)
# 3.3 绘制画面
screen.fill((10, 10, 30)) # 清屏
# (绘制各种元素)
# 3.4 刷新显示
pygame.display.flip()
clock.tick(60) # 限制帧率60FPS
# 4. 清理
pygame.quit()
sys.exit()

1.3 窗口与事件处理
import pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
# 窗口关闭
if event.type == pygame.QUIT:
running = False
# 键盘按下
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
elif event.key == pygame.K_SPACE:
print("空格键被按下")
elif event.key == pygame.K_UP:
print("上方向键")
# 键盘松开
if event.type == pygame.KEYUP:
if event.key == pygame.K_SPACE:
print("空格键被松开")
# 鼠标点击
if event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
button = event.button # 1=左键, 2=中键, 3=右键
print(f"鼠标点击: {pos}, 按钮: {button}")
# 鼠标移动
if event.type == pygame.MOUSEMOTION:
pos = pygame.mouse.get_pos()
# print(f"鼠标位置: {pos}")
# 鼠标松开
if event.type == pygame.MOUSEBUTTONUP:
print("鼠标松开")
# 持续按住检测
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
print("左键持续按下")
if keys[pygame.K_RIGHT]:
print("右键持续按下")
screen.fill((10, 10, 30))
pygame.display.flip()
clock.tick(60)
pygame.quit()
常用事件类型:
| 事件 | 常量 | 属性 |
|---|---|---|
| 窗口关闭 | QUIT |
无 |
| 键盘按下 | KEYDOWN |
key, mod, unicode |
| 键盘松开 | KEYUP |
key, mod |
| 鼠标点击 | MOUSEBUTTONDOWN |
pos, button |
| 鼠标松开 | MOUSEBUTTONUP |
pos, button |
| 鼠标移动 | MOUSEMOTION |
pos, rel, buttons |
常用按键常量:
| 按键 | 常量 |
|---|---|
| 方向键 | K_UP, K_DOWN, K_LEFT, K_RIGHT |
| 空格 | K_SPACE |
| 回车 | K_RETURN |
| ESC | K_ESCAPE |
| A-Z | K_a ~ K_z |
| 0-9 | K_0 ~ K_9 |
| Shift | K_LSHIFT, K_RSHIFT |
| Ctrl | K_LCTRL, K_RCTRL |
1.4 绘制图形与文字
import pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption('PyGame 绘图示例')
# 颜色定义
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
CYAN = (0, 255, 255)
MAGENTA = (255, 0, 255)
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((30, 30, 30))
# 绘制线条
pygame.draw.line(screen, WHITE, (50, 50), (300, 50), 3)
# 绘制矩形 (Surface, color, (x, y, w, h), width)
pygame.draw.rect(screen, RED, (50, 100, 200, 100)) # 实心
pygame.draw.rect(screen, GREEN, (300, 100, 200, 100), 3) # 空心(线宽=3)
# 绘制圆形 (Surface, color, center, radius, width)
pygame.draw.circle(screen, BLUE, (150, 350), 60) # 实心
pygame.draw.circle(screen, YELLOW, (400, 350), 60, 3) # 空心
# 绘制椭圆 (Surface, color, (x,y,w,h), width)
pygame.draw.ellipse(screen, CYAN, (500, 100, 200, 100), 2)
# 绘制多边形 (Surface, color, points, width)
points = [(500, 300), (550, 250), (600, 300), (575, 350), (525, 350)]
pygame.draw.polygon(screen, MAGENTA, points)
# 绘制弧线
import math
rect = pygame.Rect(600, 400, 150, 100)
pygame.draw.arc(screen, WHITE, rect, 0, math.pi, 2)
# 绘制文字
font = pygame.font.SysFont('SimHei', 36) # 使用系统字体
text = font.render('PyGame 绘图示例', True, WHITE)
screen.blit(text, (250, 20))
small_font = pygame.font.SysFont('SimHei', 20)
label = small_font.render('矩形 / 圆形 / 椭圆 / 多边形', True, (180, 180, 180))
screen.blit(label, (220, 560))
pygame.display.flip()
clock.tick(60)
pygame.quit()
绘图函数速查:
| 函数 | 说明 | 参数 |
|---|---|---|
draw.line(surface, color, start, end, width) |
线段 | 起点、终点 |
draw.lines(surface, color, closed, points, width) |
折线 | 点列表 |
draw.rect(surface, color, rect, width) |
矩形 | (x,y,w,h) |
draw.circle(surface, color, center, radius, width) |
圆 | 圆心、半径 |
draw.ellipse(surface, color, rect, width) |
椭圆 | 外接矩形 |
draw.polygon(surface, color, points, width) |
多边形 | 顶点列表 |
draw.arc(surface, color, rect, start, end, width) |
弧线 | 角度(弧度) |
draw.aaline |
抗锯齿线段 | 同 line |
1.5 精灵与碰撞检测
import pygame
import random
pygame.init()
screen = pygame.display.set_mode((800, 600))
# 定义玩家精灵
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((50, 50))
self.image.fill((0, 200, 255))
self.rect = self.image.get_rect()
self.rect.center = (400, 500)
self.speed = 5
def update(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and self.rect.left > 0:
self.rect.x -= self.speed
if keys[pygame.K_RIGHT] and self.rect.right < 800:
self.rect.x += self.speed
if keys[pygame.K_UP] and self.rect.top > 0:
self.rect.y -= self.speed
if keys[pygame.K_DOWN] and self.rect.bottom < 600:
self.rect.y += self.speed
# 定义敌人精灵
class Enemy(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((30, 30))
self.image.fill((255, 50, 50))
self.rect = self.image.get_rect()
self.rect.x = random.randint(0, 770)
self.rect.y = random.randint(-200, -30)
self.speed = random.randint(2, 5)
def update(self):
self.rect.y += self.speed
if self.rect.top > 600:
self.rect.x = random.randint(0, 770)
self.rect.y = random.randint(-100, -30)
# 创建精灵组
player = Player()
enemies = pygame.sprite.Group()
for _ in range(10):
enemies.add(Enemy())
all_sprites = pygame.sprite.Group()
all_sprites.add(player)
all_sprites.add(enemies)
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 更新所有精灵
all_sprites.update()
# 碰撞检测
hits = pygame.sprite.spritecollide(player, enemies, False)
if hits:
print("碰撞了!")
# 这里可以处理碰撞逻辑
# 绘制
screen.fill((10, 10, 30))
all_sprites.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()
碰撞检测方法:
| 方法 | 说明 | 适用 |
|---|---|---|
spritecollide(sprite, group, dokill) |
精灵与组碰撞 | 1对多 |
groupcollide(group1, group2, dokill1, dokill2) |
组与组碰撞 | 多对多 |
spritecollideany(sprite, group) |
任一碰撞 | 1对多(只需判断有无) |
Rect.colliderect(Rect) |
矩形碰撞 | 精确矩形 |
Rect.collidepoint(x, y) |
点碰撞 | 鼠标点击 |
Rect.collidecircle() |
圆碰撞 | 需要自定义 |
1.6 声音与音乐
import pygame
pygame.init()
# 音效 (短声音,如爆炸、点击)
jump_sound = pygame.mixer.Sound('jump.wav')
jump_sound.set_volume(0.5) # 设置音量 0.0~1.0
jump_sound.play() # 播放一次
# 背景音乐 (长音乐,如BGM)
pygame.mixer.music.load('background.mp3')
pygame.mixer.music.set_volume(0.3)
pygame.mixer.music.play(-1) # -1 = 无限循环
# 音乐控制
pygame.mixer.music.pause() # 暂停
pygame.mixer.music.unpause() # 恢复
pygame.mixer.music.stop() # 停止
pygame.mixer.music.fadeout(2000) # 2秒淡出
1.7 实战案例:烟花粒子效果
配套代码 06_pygame_demo.py 实现了一个完整的烟花粒子效果:
"""
PyGame 烟花粒子效果
核心思想:发射烟花 → 到达顶点 → 爆炸成粒子 → 粒子受重力下落 → 消亡
"""
import pygame
import random
import math
# 初始化
pygame.init()
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
COLORS = [
(255, 0, 0), (0, 255, 0), (0, 0, 255),
(255, 255, 0), (255, 0, 255), (0, 255, 255),
(255, 165, 0), (255, 105, 180), (0, 255, 128),
]
class Particle:
"""粒子类"""
def __init__(self, x, y, color):
self.x = x
self.y = y
self.color = color
angle = random.uniform(0, 2 * math.pi)
speed = random.uniform(2, 8)
self.vx = math.cos(angle) * speed
self.vy = math.sin(angle) * speed
self.life = random.randint(30, 80)
self.max_life = self.life
self.size = random.randint(2, 5)
def update(self):
self.x += self.vx
self.y += self.vy
self.vy += 0.05 # 重力
self.vx *= 0.99 # 阻力
self.life -= 1
def draw(self, surface):
alpha = self.life / self.max_life
color = tuple(int(c * alpha) for c in self.color)
size = max(1, int(self.size * alpha))
pygame.draw.circle(surface, color, (int(self.x), int(self.y)), size)
def is_alive(self):
return self.life > 0
class Firework:
"""烟花类"""
def __init__(self):
self.x = random.randint(100, WIDTH - 100)
self.y = HEIGHT
self.target_y = random.randint(100, HEIGHT // 2)
self.speed = random.uniform(8, 12)
self.color = random.choice(COLORS)
self.exploded = False
self.particles = []
def update(self):
if not self.exploded:
self.y -= self.speed
if self.y <= self.target_y:
self.explode()
else:
for p in self.particles:
p.update()
self.particles = [p for p in self.particles if p.is_alive()]
def explode(self):
self.exploded = True
for _ in range(random.randint(50, 100)):
self.particles.append(Particle(self.x, self.y, self.color))
def draw(self, surface):
if not self.exploded:
pygame.draw.circle(surface, self.color,
(int(self.x), int(self.y)), 4)
else:
for p in self.particles:
p.draw(surface)
# 主循环
fireworks = []
clock = pygame.time.Clock()
for _ in range(300): # 运行约5秒
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
if _ % 20 == 0:
fireworks.append(Firework())
for fw in fireworks:
fw.update()
fireworks = [fw for fw in fireworks if not fw.is_dead() if hasattr(fw, 'is_dead') else not (fw.exploded and len(fw.particles) == 0)]
screen.fill((10, 10, 30))
for fw in fireworks:
fw.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()
第二章:Turtle —— 入门级绘图
2.1 Turtle 简介
Turtle 是 Python 内置的绘图模块(无需安装),非常适合编程入门教学。它源自 Logo 语言,通过控制一只"海龟"在画布上移动来绘制图形。
import turtle
Turtle 的核心概念:

2.2 基本绘图命令
import turtle
# 创建画布和海龟
screen = turtle.Screen()
screen.title('Turtle 绘图')
t = turtle.Turtle()
t.speed(3) # 1-10, 0=最快
# 移动命令
t.forward(100) # 前进100
t.backward(50) # 后退50
t.right(90) # 右转90度
t.left(90) # 左转90度
# 移动到指定位置
t.penup() # 抬起画笔(移动时不画线)
t.goto(100, 100) # 移动到(100, 100)
t.pendown() # 放下画笔
# 画圆
t.circle(50) # 半径50的圆
t.circle(50, 180) # 半径50的半圆
# 设置画笔
t.pensize(3) # 画笔粗细
t.pencolor('red') # 画笔颜色
t.fillcolor('blue') # 填充颜色
# 填充
t.begin_fill()
t.circle(50)
t.end_fill()
# 隐藏/显示海龟
t.hideturtle()
t.showturtle()
# 重置
t.reset() # 重置画笔
t.clear() # 清除画迹但不重置
# 完成
screen.mainloop()
2.3 绘制复杂图形
正多边形
import turtle
t = turtle.Turtle()
t.speed(5)
def draw_polygon(sides, length):
"""绘制正多边形"""
angle = 360 / sides
for _ in range(sides):
t.forward(length)
t.right(angle)
# 绘制多种正多边形
colors = ['red', 'orange', 'yellow', 'green', 'blue', 'purple']
for i in range(3, 9):
t.pencolor(colors[i - 3])
draw_polygon(i, 80)
t.penup()
t.forward(120)
t.pendown()
turtle.done()
五角星
import turtle
t = turtle.Turtle()
t.speed(5)
t.pencolor('red')
t.fillcolor('yellow')
t.pensize(3)
t.begin_fill()
for _ in range(5):
t.forward(150)
t.right(144) # 五角星的角度
t.end_fill()
turtle.done()
螺旋
import turtle
t = turtle.Turtle()
t.speed(0)
colors = ['red', 'orange', 'yellow', 'green', 'blue', 'purple']
for i in range(360):
t.pencolor(colors[i % 6])
t.forward(i * 0.5)
t.right(61) # 黄金角度
turtle.done()
2.4 分形与递归
Turtle 非常适合绘制递归和分形图形:
递归树
import turtle
t = turtle.Turtle()
t.speed(0)
t.left(90)
def draw_tree(length, depth):
if depth == 0:
return
t.forward(length)
t.left(30)
draw_tree(length * 0.7, depth - 1)
t.right(60)
draw_tree(length * 0.7, depth - 1)
t.left(30)
t.backward(length)
draw_tree(100, 7)
turtle.done()
科赫雪花
import turtle
t = turtle.Turtle()
t.speed(0)
def koch_curve(length, depth):
if depth == 0:
t.forward(length)
return
koch_curve(length / 3, depth - 1)
t.left(60)
koch_curve(length / 3, depth - 1)
t.right(120)
koch_curve(length / 3, depth - 1)
t.left(60)
koch_curve(length / 3, depth - 1)
def koch_snowflake(length, depth):
for _ in range(3):
koch_curve(length, depth)
t.right(120)
t.penup()
t.goto(-150, 50)
t.pendown()
koch_snowflake(300, 4)
turtle.done()
分形图形原理:

第三章:openpyxl —— Excel 处理
3.1 读写 Excel 基本操作
openpyxl 是读写 Excel 2007+ (.xlsx) 文件的 Python 库。它是处理 Excel 文件最常用的库之一。
pip install openpyxl
from openpyxl import Workbook, load_workbook
# ─── 创建新工作簿 ───
wb = Workbook()
# 获取活动工作表
ws = wb.active
ws.title = '员工数据'
# 写入标题行
headers = ['姓名', '年龄', '部门', '薪资', '入职日期', '评分']
ws.append(headers)
# 写入数据
employees = [
['张三', 25, '技术部', 15000, '2020-03-15', 88.5],
['李四', 30, '市场部', 18000, '2019-07-01', 92.3],
['王五', 35, '技术部', 25000, '2017-11-20', 95.1],
['赵六', 28, '人事部', 12000, '2021-06-10', 76.8],
['孙七', 42, '市场部', 30000, '2015-02-28', 91.0],
]
for emp in employees:
ws.append(emp)
# 保存
wb.save('employees.xlsx')
print("工作簿已保存")
# ─── 读取已有工作簿 ───
wb = load_workbook('employees.xlsx')
ws = wb.active
# 读取所有数据
for row in ws.iter_rows(min_row=1, values_only=True):
print(row)
# 读取指定范围
for row in ws['A2:D6']:
values = [cell.value for cell in row]
print(values)
# 读取单个单元格
print(ws['A2'].value) # 张三
print(ws['C4'].value) # 技术部
print(ws.cell(3, 2).value) # 30 (行3,列2)
# 获取行列信息
print(f"最大行: {ws.max_row}")
print(f"最大列: {ws.max_column}")
单元格操作速查:
| 操作 | 代码 | 说明 |
|---|---|---|
| 读取值 | ws['A1'].value |
获取 A1 的值 |
| 写入值 | ws['A1'] = 'Hello' |
设置 A1 的值 |
| 按位置访问 | ws.cell(row=1, column=1) |
行列从1开始 |
| 遍历行 | ws.iter_rows(values_only=True) |
逐行遍历 |
| 遍历列 | ws.iter_cols(values_only=True) |
逐列遍历 |
| 插入行 | ws.insert_rows(2) |
在第2行前插入 |
| 删除行 | ws.delete_rows(2) |
删除第2行 |
| 合并单元格 | ws.merge_cells('A1:D1') |
合并 |
| 拆分单元格 | ws.unmerge_cells('A1:D1') |
拆分 |
3.2 样式与格式化
from openpyxl import Workbook
from openpyxl.styles import (
Font, PatternFill, Alignment, Border, Side, numbers
)
wb = Workbook()
ws = wb.active
# ─── 字体样式 ───
title_font = Font(
name='微软雅黑',
size=16,
bold=True,
color='FFFFFF'
)
header_font = Font(name='微软雅黑', size=11, bold=True, color='333333')
data_font = Font(name='微软雅黑', size=10)
# ─── 填充样式 ───
title_fill = PatternFill(start_color='2C3E50', end_color='2C3E50', fill_type='solid')
header_fill = PatternFill(start_color='3498DB', end_color='3498DB', fill_type='solid')
row_fill = PatternFill(start_color='ECF0F1', end_color='ECF0F1', fill_type='solid')
# ─── 对齐方式 ───
center_align = Alignment(horizontal='center', vertical='center', wrap_text=True)
left_align = Alignment(horizontal='left', vertical='center')
# ─── 边框样式 ───
thin_border = Border(
left=Side(style='thin', color='BDC3C7'),
right=Side(style='thin', color='BDC3C7'),
top=Side(style='thin', color='BDC3C7'),
bottom=Side(style='thin', color='BDC3C7')
)
# 写入标题
ws.merge_cells('A1:F1')
ws['A1'] = '员工薪资报表 - 2025年5月'
ws['A1'].font = title_font
ws['A1'].fill = title_fill
ws['A1'].alignment = center_align
# 写入表头
headers = ['姓名', '年龄', '部门', '薪资', '入职日期', '评分']
for col, header in enumerate(headers, 1):
cell = ws.cell(row=2, column=col, value=header)
cell.font = header_font
cell.fill = header_fill
cell.alignment = center_align
cell.border = thin_border
# 写入数据(带交替行颜色)
data = [
['张三', 25, '技术部', 15000, '2020-03-15', 88.5],
['李四', 30, '市场部', 18000, '2019-07-01', 92.3],
['王五', 35, '技术部', 25000, '2017-11-20', 95.1],
]
for row_idx, row_data in enumerate(data, 3):
for col_idx, value in enumerate(row_data, 1):
cell = ws.cell(row=row_idx, column=col_idx, value=value)
cell.font = data_font
cell.alignment = center_align
cell.border = thin_border
if row_idx % 2 == 1: # 奇数行加背景
cell.fill = row_fill
# 薪资列格式化
if col_idx == 4:
cell.number_format = '#,##0'
# 设置列宽
col_widths = {'A': 12, 'B': 8, 'C': 10, 'D': 12, 'E': 15, 'F': 8}
for col, width in col_widths.items():
ws.column_dimensions[col].width = width
# 设置行高
ws.row_dimensions[1].height = 35
ws.row_dimensions[2].height = 25
wb.save('styled_report.xlsx')
print("带样式的报表已保存")
3.3 公式与图表
from openpyxl import Workbook
from openpyxl.chart import BarChart, LineChart, PieChart, Reference
from openpyxl.utils import get_column_letter
wb = Workbook()
ws = wb.active
ws.title = '销售数据'
# 写入数据
data = [
['月份', '产品A', '产品B', '产品C', '总计'],
['1月', 120, 85, 60],
['2月', 135, 92, 68],
['3月', 142, 98, 75],
['4月', 155, 105, 82],
['5月', 168, 112, 90],
['6月', 180, 120, 95],
]
for row_idx, row_data in enumerate(data, 1):
for col_idx, value in enumerate(row_data, 1):
ws.cell(row=row_idx, column=col_idx, value=value)
# 添加求和公式
for row in range(3, 9):
ws.cell(row=row, column=5).value = f'=SUM(B{row}:D{row})'
# 添加总计行
ws.cell(row=9, column=1, value='总计').font = Font(bold=True)
for col in range(2, 6):
col_letter = get_column_letter(col)
ws.cell(row=9, column=col).value = f'=SUM({col_letter}3:{col_letter}8)'
ws.cell(row=9, column=col).font = Font(bold=True)
# ─── 柱状图 ───
bar_chart = BarChart()
bar_chart.type = 'col'
bar_chart.title = '月度销售对比'
bar_chart.y_axis.title = '销售额 (万元)'
bar_chart.x_axis.title = '月份'
bar_chart.style = 10
data_ref = Reference(ws, min_col=2, min_row=2, max_col=4, max_row=8)
cats = Reference(ws, min_col=1, min_row=3, max_row=8)
bar_chart.add_data(data_ref, titles_from_data=True)
bar_chart.set_categories(cats)
bar_chart.shape = 4
ws.add_chart(bar_chart, 'A11')
# ─── 折线图 ───
line_chart = LineChart()
line_chart.title = '销售趋势'
line_chart.y_axis.title = '销售额'
line_chart.style = 11
line_chart.width = 15
line_chart.height = 10
data_ref = Reference(ws, min_col=2, min_row=2, max_col=4, max_row=8)
line_chart.add_data(data_ref, titles_from_data=True)
line_chart.set_categories(cats)
ws.add_chart(line_chart, 'A28')
# ─── 饼图 ───
pie_ws = wb.create_sheet('占比')
pie_ws['A1'] = '产品'
pie_ws['B1'] = '总销售额'
products = [('产品A', 900), ('产品B', 612), ('产品C', 470)]
for i, (name, sales) in enumerate(products, 2):
pie_ws.cell(row=i, column=1, value=name)
pie_ws.cell(row=i, column=2, value=sales)
pie = PieChart()
pie.title = '产品销售占比'
labels = Reference(pie_ws, min_col=1, min_row=2, max_row=4)
values = Reference(pie_ws, min_col=2, min_row=2, max_row=4)
pie.add_data(values)
pie.set_categories(labels)
pie_ws.add_chart(pie, 'D2')
wb.save('sales_report.xlsx')
print("带图表的报表已保存")
openpyxl 支持的图表类型:
| 类型 | 类名 | 说明 |
|---|---|---|
| 柱状图 | BarChart |
垂直/水平柱状 |
| 折线图 | LineChart |
趋势变化 |
| 饼图 | PieChart |
占比分析 |
| 面积图 | AreaChart |
堆叠面积 |
| 散点图 | ScatterChart |
变量关系 |
| 气泡图 | BubbleChart |
三维散点 |
| 雷达图 | RadarChart |
多维度对比 |
3.4 实战:批量生成报表
"""
批量生成月度销售报表
根据原始数据,自动生成带格式的 Excel 报表
"""
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.chart import BarChart, Reference
from datetime import datetime
def generate_sales_report(data, month, filename):
"""生成单月销售报表"""
wb = Workbook()
ws = wb.active
ws.title = f'{month}月销售'
# 表头
ws.merge_cells('A1:F1')
ws['A1'] = f'{month}月销售报表'
ws['A1'].font = Font(name='微软雅黑', size=18, bold=True, color='FFFFFF')
ws['A1'].fill = PatternFill('solid', fgColor='2C3E50')
ws['A1'].alignment = Alignment(horizontal='center', vertical='center')
ws.row_dimensions[1].height = 45
# 副标题
ws.merge_cells('A2:F2')
ws['A2'] = f'生成时间: {datetime.now().strftime("%Y-%m-%d %H:%M")}'
ws['A2'].font = Font(size=10, color='7F8C8D')
ws['A2'].alignment = Alignment(horizontal='right')
# 表头行
headers = ['排名', '产品名称', '销量', '单价', '销售额', '环比增长']
header_fill = PatternFill('solid', fgColor='3498DB')
header_font = Font(name='微软雅黑', size=11, bold=True, color='FFFFFF')
thin = Side(style='thin', color='BDC3C7')
border = Border(left=thin, right=thin, top=thin, bottom=thin)
for col, header in enumerate(headers, 1):
cell = ws.cell(row=4, column=col, value=header)
cell.font = header_font
cell.fill = header_fill
cell.alignment = Alignment(horizontal='center', vertical='center')
cell.border = border
# 数据行
for i, item in enumerate(data):
row = i + 5
ws.cell(row=row, column=1, value=i+1)
ws.cell(row=row, column=2, value=item['name'])
ws.cell(row=row, column=3, value=item['quantity'])
ws.cell(row=row, column=4, value=item['price'])
ws.cell(row=row, column=5, value=f'=C{row}*D{row}') # 公式
ws.cell(row=row, column=6, value=f'{item.get("growth", 0)}%')
for col in range(1, 7):
cell = ws.cell(row=row, column=col)
cell.alignment = Alignment(horizontal='center', vertical='center')
cell.border = border
if i % 2 == 1:
cell.fill = PatternFill('solid', fgColor='F8F9FA')
# 总计行
total_row = len(data) + 5
ws.cell(row=total_row, column=2, value='总计').font = Font(bold=True)
ws.cell(row=total_row, column=3, value=f'=SUM(C5:C{total_row-1})')
ws.cell(row=total_row, column=5, value=f'=SUM(E5:E{total_row-1})')
# 设置列宽
for col, width in [('A', 8), ('B', 20), ('C', 12), ('D', 12), ('E', 15), ('F', 12)]:
ws.column_dimensions[col].width = width
# 添加图表
chart = BarChart()
chart.title = f'{month}月产品销售额'
chart.y_axis.title = '销售额 (元)'
data_ref = Reference(ws, min_col=5, min_row=4, max_row=total_row-1)
cats = Reference(ws, min_col=2, min_row=5, max_row=total_row-1)
chart.add_data(data_ref, titles_from_data=True)
chart.set_categories(cats)
ws.add_chart(chart, f'A{total_row + 2}')
wb.save(filename)
print(f'报表已生成: {filename}')
# 使用示例
sample_data = [
{'name': '笔记本电脑', 'quantity': 150, 'price': 5999, 'growth': 12},
{'name': '无线耳机', 'quantity': 320, 'price': 899, 'growth': 25},
{'name': '智能手表', 'quantity': 180, 'price': 2499, 'growth': 8},
{'name': '平板电脑', 'quantity': 95, 'price': 3299, 'growth': -3},
{'name': '机械键盘', 'quantity': 420, 'price': 459, 'growth': 18},
]
generate_sales_report(sample_data, 5, 'may_sales_report.xlsx')
第四章:python-docx —— Word 文档处理
4.1 创建文档
python-docx 可以用 Python 创建和修改 Word (.docx) 文档:
pip install python-docx
from docx import Document
from docx.shared import Inches, Pt, Cm, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.style import WD_STYLE_TYPE
# 创建文档
doc = Document()
# 设置标题
doc.add_heading('Python 文档处理示例', level=0)
# 添加段落
p1 = doc.add_paragraph('这是一个普通段落。')
p1.add_run('这段文字加粗了。').bold = True
p1.add_run(' 这段是斜体。').italic = True
# 带样式的段落
doc.add_paragraph('这是引用段落。', style='Intense Quote')
# 设置字体
p = doc.add_paragraph()
run = p.add_run('自定义样式的文字')
run.font.name = '微软雅黑'
run.font.size = Pt(14)
run.font.color.rgb = RGBColor(0x2C, 0x3E, 0x50)
run.bold = True
# 对齐方式
centered = doc.add_paragraph('居中对齐的文字')
centered.alignment = WD_ALIGN_PARAGRAPH.CENTER
# 保存
doc.save('example.docx')
print("Word 文档已创建")
4.2 表格与列表
from docx import Document
from docx.shared import Inches, Pt
from docx.enum.table import WD_TABLE_ALIGNMENT
doc = Document()
doc.add_heading('员工信息表', level=1)
# ─── 创建表格 ───
table = doc.add_table(rows=5, cols=4)
table.style = 'Medium Shading 1 Accent 1'
table.alignment = WD_TABLE_ALIGNMENT.CENTER
# 设置表头
headers = ['姓名', '部门', '职位', '薪资']
for i, header in enumerate(headers):
cell = table.rows[0].cells[i]
cell.text = header
for paragraph in cell.paragraphs:
for run in paragraph.runs:
run.bold = True
# 填充数据
data = [
['张三', '技术部', '高级工程师', '25,000'],
['李四', '市场部', '市场经理', '22,000'],
['王五', '技术部', '架构师', '35,000'],
['赵六', '人事部', 'HR主管', '18,000'],
]
for row_idx, row_data in enumerate(data, 1):
for col_idx, value in enumerate(row_data):
table.rows[row_idx].cells[col_idx].text = value
# ─── 添加列表 ───
doc.add_heading('项目清单', level=2)
# 无序列表
doc.add_paragraph('项目A', style='List Bullet')
doc.add_paragraph('项目B', style='List Bullet')
doc.add_paragraph('项目C', style='List Bullet')
# 有序列表
doc.add_heading('操作步骤', level=2)
doc.add_paragraph('第一步:准备数据', style='List Number')
doc.add_paragraph('第二步:处理数据', style='List Number')
doc.add_paragraph('第三步:生成报告', style='List Number')
# ─── 添加分页符 ───
doc.add_page_break()
# ─── 添加图片 ───
doc.add_heading('示意图', level=2)
try:
doc.add_picture('images/01_创建图像.png', width=Inches(5))
except FileNotFoundError:
doc.add_paragraph('[图片未找到]')
doc.save('report.docx')
print("带表格的 Word 文档已创建")
4.3 实战:批量生成合同
"""
批量生成劳动合同
根据模板填充员工信息,生成个性化合同
"""
from docx import Document
from docx.shared import Pt, Cm
from docx.enum.text import WD_ALIGN_PARAGRAPH
from datetime import datetime
def generate_contract(employee_info, template_path=None, output_path=None):
"""生成劳动合同"""
doc = Document()
# 标题
title = doc.add_paragraph()
title.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = title.add_run('劳 动 合 同 书')
run.font.size = Pt(22)
run.bold = True
doc.add_paragraph() # 空行
# 合同编号
contract_no = doc.add_paragraph()
contract_no.alignment = WD_ALIGN_PARAGRAPH.RIGHT
contract_no.add_run(f'合同编号: LC-{employee_info["id"]:06d}')
# 正文内容
intro = doc.add_paragraph()
intro.add_run('甲方(用人单位): ').bold = True
intro.add_run('XX科技有限公司\n')
intro.add_run('乙方(劳动者): ').bold = True
intro.add_run(f'{employee_info["name"]}\n')
doc.add_paragraph()
# 基本信息
doc.add_heading('第一条 基本信息', level=2)
info = doc.add_paragraph()
info.add_run(f'身份证号: {employee_info["id_number"]}\n')
info.add_run(f'入职日期: {employee_info["hire_date"]}\n')
info.add_run(f'部门: {employee_info["department"]}\n')
info.add_run(f'职位: {employee_info["position"]}\n')
info.add_run(f'薪资: ¥{employee_info["salary"]:,}/月\n')
# 合同期限
doc.add_heading('第二条 合同期限', level=2)
term = doc.add_paragraph()
term.add_run(
f'本合同为固定期限劳动合同,期限自 {employee_info["hire_date"]} '
f'起至 {employee_info["end_date"]} 止,共计 {employee_info["duration"]} 年。'
)
# 工作内容
doc.add_heading('第三条 工作内容', level=2)
doc.add_paragraph(
f'乙方同意根据甲方工作需要,担任 {employee_info["position"]} '
f'岗位工作,工作地点为甲方公司所在地。'
)
# 薪酬待遇
doc.add_heading('第四条 薪酬待遇', level=2)
doc.add_paragraph(
f'甲方每月 {employee_info["pay_day"]} 日以货币形式支付乙方工资,'
f'月工资为人民币 {employee_info["salary"]:,} 元。'
)
# 签字区域
doc.add_paragraph()
doc.add_paragraph()
signatures = doc.add_paragraph()
signatures.add_run(f'甲方签章: ________________ ').bold = True
signatures.add_run(f'乙方签名: {employee_info["name"]}\n\n')
signatures.add_run(f'日期: {datetime.now().strftime("%Y年%m月%d日")} ')
signatures.add_run(f'日期: ________________')
# 保存
if output_path is None:
output_path = f'contracts/contract_{employee_info["name"]}.docx'
import os
os.makedirs(os.path.dirname(output_path) or '.', exist_ok=True)
doc.save(output_path)
print(f'合同已生成: {output_path}')
# 批量生成
employees = [
{
'id': 1001, 'name': '张三', 'id_number': '110101199001011234',
'hire_date': '2025年6月1日', 'end_date': '2027年5月31日', 'duration': 2,
'department': '技术部', 'position': '高级工程师',
'salary': 25000, 'pay_day': 15
},
{
'id': 1002, 'name': '李四', 'id_number': '310101198805051234',
'hire_date': '2025年6月1日', 'end_date': '2027年5月31日', 'duration': 2,
'department': '市场部', 'position': '市场经理',
'salary': 22000, 'pay_day': 15
},
{
'id': 1003, 'name': '王五', 'id_number': '440101199203031234',
'hire_date': '2025年6月1日', 'end_date': '2028年5月31日', 'duration': 3,
'department': '技术部', 'position': '架构师',
'salary': 35000, 'pay_day': 15
},
]
for emp in employees:
generate_contract(emp, output_path=f'contracts/contract_{emp["name"]}.docx')
print(f"\n已生成 {len(employees)} 份合同")
第五章:PyPDF2 —— PDF 处理
5.1 PDF 基本操作
PyPDF2(现已升级为 pypdf)可以处理 PDF 文件的读取、合并、拆分等操作:
pip install PyPDF2
# 或新版
pip install pypdf
from PyPDF2 import PdfReader, PdfWriter
# ─── 读取 PDF ───
reader = PdfReader('document.pdf')
print(f"页数: {len(reader.pages)}")
# 读取文本
for i, page in enumerate(reader.pages):
text = page.extract_text()
print(f"第 {i+1} 页:")
print(text[:200]) # 前200字符
# 读取元数据
metadata = reader.metadata
print(f"标题: {metadata.title}")
print(f"作者: {metadata.author}")
print(f"创建者: {metadata.creator}")
# ─── 提取特定页 ───
page = reader.pages[0]
text = page.extract_text()
print(text)
# ─── 提取图片 ───
for i, page in enumerate(reader.pages):
for image in page.images:
with open(f'image_{i}_{image.name}', 'wb') as f:
f.write(image.data)
print(f"提取图片: image_{i}_{image.name}")
5.2 PDF 合并与拆分
from PyPDF2 import PdfReader, PdfWriter
import os
# ─── 合并多个 PDF ───
def merge_pdfs(pdf_list, output_path):
"""合并多个PDF文件"""
writer = PdfWriter()
for pdf_path in pdf_list:
reader = PdfReader(pdf_path)
for page in reader.pages:
writer.add_page(page)
with open(output_path, 'wb') as f:
writer.write(f)
print(f"合并完成: {output_path}")
# 使用
merge_pdfs(['report1.pdf', 'report2.pdf', 'report3.pdf'], 'merged_report.pdf')
# ─── 拆分 PDF ───
def split_pdf(input_path, output_dir):
"""将PDF拆分为单页"""
os.makedirs(output_dir, exist_ok=True)
reader = PdfReader(input_path)
for i, page in enumerate(reader.pages):
writer = PdfWriter()
writer.add_page(page)
output_path = os.path.join(output_dir, f'page_{i+1:03d}.pdf')
with open(output_path, 'wb') as f:
writer.write(f)
print(f"拆分: {output_path}")
split_pdf('merged_report.pdf', 'split_pages')
# ─── 提取特定页 ───
def extract_pages(input_path, page_numbers, output_path):
"""提取指定页"""
reader = PdfReader(input_path)
writer = PdfWriter()
for page_num in page_numbers:
writer.add_page(reader.pages[page_num - 1]) # 页码从1开始
with open(output_path, 'wb') as f:
writer.write(f)
print(f"提取完成: {output_path}")
extract_pages('merged_report.pdf', [1, 3, 5], 'selected_pages.pdf')
# ─── 添加水印 ───
def add_watermark(input_path, watermark_path, output_path):
"""给PDF添加水印"""
reader = PdfReader(input_path)
watermark = PdfReader(watermark_path)
writer = PdfWriter()
for page in reader.pages:
page.merge_page(watermark.pages[0])
writer.add_page(page)
with open(output_path, 'wb') as f:
writer.write(f)
print(f"水印已添加: {output_path}")
PyPDF2 功能速查:
| 功能 | 方法 | 说明 |
|---|---|---|
| 读取 | PdfReader(path) |
打开PDF |
| 页数 | len(reader.pages) |
总页数 |
| 提取文本 | page.extract_text() |
获取页面文字 |
| 合并 | writer.add_page(page) |
添加页面 |
| 拆分 | 单独保存每页 | 拆分为多文件 |
| 加密 | writer.encrypt(password) |
设置密码 |
| 水印 | page.merge_page(watermark) |
叠加水印 |
| 旋转 | page.rotate(90) |
旋转页面 |
总结
工具选择速查表
| 你的需求 | 推荐工具 | 安装命令 |
|---|---|---|
| 开发2D游戏 | PyGame | pip install pygame |
| 教学绘图 | Turtle | 内置,无需安装 |
| 处理 Excel | openpyxl | pip install openpyxl |
| 生成 Word | python-docx | pip install python-docx |
| 处理 PDF | PyPDF2 | pip install PyPDF2 |
办公自动化流程

完整安装命令
pip install pygame openpyxl python-docx PyPDF2
系列总结
至此,我们完成了 Python 常用工具库实战教程的全部 6 篇文章:
| 篇章 | 主题 | 核心库 |
|---|---|---|
| 第一篇 | 科学计算与数据分析 | NumPy, Pandas, SciPy |
| 第二篇 | 数据可视化 | Matplotlib, Seaborn, Plotly |
| 第三篇 | 图像处理 | Pillow, OpenCV |
| 第四篇 | 网络请求与爬虫 | Requests, BeautifulSoup, Selenium, Scrapy |
| 第五篇 | 实用工具与机器学习 | Rich, Tqdm, Faker, Schedule, Scikit-learn |
| 第六篇 | 游戏图形与文件处理 | PyGame, Turtle, openpyxl, python-docx, PyPDF2 |
这 6 篇文章覆盖了 30+ 个 Python 库,从数据科学到 Web 开发,从图像处理到游戏开发,从自动化办公到机器学习。希望能帮助你快速建立 Python 工具库的知识体系!
祝你编程愉快! 🚀
更多推荐

所有评论(0)