Python 游戏开发与文件处理:PyGame + Turtle + openpyxl + python-docx + PyPDF2

Python 游戏开发与文件处理:PyGame + Turtle + openpyxl + python-docx + PyPDF2

专栏:Python 常用工具库实战教程 | 第六篇

适合人群:Python 趣味编程爱好者、办公自动化需求者


目录

  • [前言:Python 不只是数据和 Web](#前言:Python 不只是数据和 Web)
  • [第一章:PyGame ------ 2D 游戏开发](#第一章:PyGame —— 2D 游戏开发)
    • [1.1 PyGame 是什么](#1.1 PyGame 是什么)
    • [1.2 PyGame 基本架构](#1.2 PyGame 基本架构)
    • [1.3 窗口与事件处理](#1.3 窗口与事件处理)
    • [1.4 绘制图形与文字](#1.4 绘制图形与文字)
    • [1.5 精灵与碰撞检测](#1.5 精灵与碰撞检测)
    • [1.6 声音与音乐](#1.6 声音与音乐)
    • [1.7 实战案例:Flappy Bird 简化版](#1.7 实战案例:Flappy Bird 简化版)
  • [第二章:Turtle ------ 入门级绘图](#第二章:Turtle —— 入门级绘图)
    • [2.1 Turtle 简介](#2.1 Turtle 简介)
    • [2.2 基本绘图命令](#2.2 基本绘图命令)
    • [2.3 绘制复杂图形](#2.3 绘制复杂图形)
    • [2.4 分形与递归](#2.4 分形与递归)
  • [第三章:openpyxl ------ Excel 处理](#第三章:openpyxl —— Excel 处理)
    • [3.1 读写 Excel 基本操作](#3.1 读写 Excel 基本操作)
    • [3.2 样式与格式化](#3.2 样式与格式化)
    • [3.3 公式与图表](#3.3 公式与图表)
    • [3.4 实战:批量生成报表](#3.4 实战:批量生成报表)
  • [第四章:python-docx ------ Word 文档处理](#第四章:python-docx —— Word 文档处理)
    • [4.1 创建文档](#4.1 创建文档)
    • [4.2 表格与列表](#4.2 表格与列表)
    • [4.3 实战:批量生成合同](#4.3 实战:批量生成合同)
  • [第五章:PyPDF2 ------ PDF 处理](#第五章:PyPDF2 —— PDF 处理)
    • [5.1 PDF 基本操作](#5.1 PDF 基本操作)
    • [5.2 PDF 合并与拆分](#5.2 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 游戏、交互式应用和多媒体程序。

bash 复制代码
pip install pygame

PyGame 的核心能力:

  • 创建和管理游戏窗口
  • 处理键盘、鼠标等输入事件
  • 绘制图形、文字和精灵(Sprite)
  • 播放声音和背景音乐
  • 碰撞检测
  • 精灵动画

1.2 PyGame 基本架构

每个 PyGame 程序都遵循相同的基本架构:

python 复制代码
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 窗口与事件处理

python 复制代码
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 绘制图形与文字

python 复制代码
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 精灵与碰撞检测

python 复制代码
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 声音与音乐

python 复制代码
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 实现了一个完整的烟花粒子效果:

python 复制代码
"""
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 语言,通过控制一只"海龟"在画布上移动来绘制图形。

python 复制代码
import turtle

Turtle 的核心概念:

2.2 基本绘图命令

python 复制代码
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 绘制复杂图形

正多边形
python 复制代码
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()
五角星
python 复制代码
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()
螺旋
python 复制代码
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 非常适合绘制递归和分形图形:

递归树
python 复制代码
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()
科赫雪花
python 复制代码
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 文件最常用的库之一。

bash 复制代码
pip install openpyxl
python 复制代码
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 样式与格式化

python 复制代码
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 公式与图表

python 复制代码
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 实战:批量生成报表

python 复制代码
"""
批量生成月度销售报表
根据原始数据,自动生成带格式的 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) 文档:

bash 复制代码
pip install python-docx
python 复制代码
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 表格与列表

python 复制代码
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 实战:批量生成合同

python 复制代码
"""
批量生成劳动合同
根据模板填充员工信息,生成个性化合同
"""
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 文件的读取、合并、拆分等操作:

bash 复制代码
pip install PyPDF2
# 或新版
pip install pypdf
python 复制代码
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 合并与拆分

python 复制代码
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

办公自动化流程

完整安装命令

bash 复制代码
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 工具库的知识体系!


祝你编程愉快! 🚀

相关推荐
Fleshy数模6 小时前
课堂教学质量评估系统:基于加权欧氏距离的评分实现
python·llm
高林雨露7 小时前
kotlin 相关code
开发语言·kotlin
_山海7 小时前
用langchain 通过text-embedding-3-small生成embedding
python·langchain·llm
我还记得那天7 小时前
函数的递归调用
c语言·开发语言·visualstudio
zhangfeng11337 小时前
ThinkPHP5 事件系统的标准最佳实践 事件系统的完整设计逻辑tags.php tags.php(事件地图)
android·开发语言·php
xyq20247 小时前
HTML 标签简写及全称
开发语言
毋语天7 小时前
FastAPI 网络编程入门到实战:从 HTTP 协议到异步 API 开发
python·网络编程·fastapi·rest api·异步编程
tongluowan0077 小时前
数据结构 Bitmap(位图)示例 - 用户签到系统
开发语言·数据结构·bitmap·用户签到系统
就叫_这个吧7 小时前
Java线程池应用的四种方式+线程池底层实现原理
java·开发语言