本項目效果初始截圖如下
動畫見本人b站投稿:https://www.bilibili.com/video/av95491382
本項目對應github地址:https://github.com/BigShuang
python版本:3.6,pygame版本:1.9.3。(python版本一致應該就沒什麼問題)
樣例gif如下
======================= 大爽歌作,made by big shuang =======================
一、思路分析
冒泡排序的思路感覺沒有什麼需要多做贅述的了。
所以這裏主要講下可視化的問題
這裏爲了方便,採用面向對象的思路編寫代碼。
其實主要需要三個類就要。
1 - 要實現一個內部有數字的圓點,同時這個圓點大小和數字相關聯。
2 - 在實現一個管理這些圓點列表的,可以認爲和是和數組相對應的列表管理者。
3 - 實現可視化
二、代碼實現
1 在開頭,先導入需要的類再設置好相關的常量
import pygame
import random
FPS = 60 # 遊戲幀率
WIN_WIDTH = 1200 # 窗口寬度
WIN_HEIGHT = 980 # 窗口高度
BUBBLE_SPACE = 40 # 氣泡之間的間距
INIT_R = 10 # 氣泡初始大小-半徑
DR = 4 # 氣泡數字值一個單位對應的氣泡半徑增加量
NUMBER = 10 # 氣泡個數
COLORS = {
"bg": (240, 255, 255), # 背景顏色
"bubble": (135, 206, 235), # 氣泡顏色
"select": (0, 139, 139), # 被選擇的氣泡顏色
"text": (0, 0, 0), # 文本顏色
}
pygame.init() # pygame 初始化,必須有,且必須在開頭
try:
# 如果你電腦自帶有Arial字體,這裏可以直接運行,(一般電腦都會有的)
font = pygame.font.SysFont("Arial", 20)
except Exception:
# 如果你本地沒有Arial字體,你需要在項目裏面建立一個font文件夾存放simkai.ttf字體文件,
# 字體文件可在我的github下載
font = pygame.font.Font("./font/simkai.ttf", 20)
2 建立Bubble類實現氣泡
建立Bubble類,實現氣泡,即一個內部有數字的圓點
class Bubble:
speed = 2 # 速度
color = COLORS["bubble"]
def __init__(self, master, x, y, v, r):
self._master = master # 父控件
self.cx = x # 氣泡中心橫座標 - x
self.cy = y # 氣泡中心縱座標 - y
self.v = v # 氣泡內部數字
self.radius = r # 氣泡半徑
# 註釋說明1-1
# 由於pygame自己繪製的圓鋸齒太大,看起來十分不光滑
# 想要光滑的圓,只能從png文件中導入圓的圖片(圖片可前往我的github下載)
# 這一部分對於單純的pygame動畫學習者而言,並不是非常重要,所以我把與此相關的代碼都給註釋掉了
# 有需要的把所有標明註釋說明1-1的代碼解註釋就好
# self.image = pygame.image.load("img/bubble.png")
# self.select_image = pygame.image.load("img/select.png")
# self.resize()
def up(self):
# 氣泡上浮
self.cy -= DR
def down(self):
# 氣泡下沉
self.cy += DR
# 繪製氣泡
def draw(self, current=False):
text = font.render(str(self.v), 1, COLORS["text"])
if current:
pygame.draw.circle(self._master, COLORS["select"], (self.cx, self.cy), self.radius)
else:
pygame.draw.circle(self._master, self.color, (self.cx, self.cy), self.radius)
text_width = text.get_width()
text_height = text.get_height()
self._master.blit(text, (self.cx - text_width // 2, self.cy - text_height // 2))
# 見註釋說明1-1
# def resize(self):
# self.image = pygame.transform.scale(self.image, (self.radius * 2, self.radius * 2))
# self.select_image = pygame.transform.scale(self.select_image, (self.radius * 2, self.radius * 2))
# def draw_pic(self, current=False):
# if current:
# self._master.blit(self.select_image, (self.cx - self.radius, self.cy - self.radius))
# else:
# self._master.blit(self.image, (self.cx - self.radius, self.cy - self.radius))
#
# text = font.render(str(self.v), 1, COLORS["text"])
# text_width = text.get_width()
# text_height = text.get_height()
# self._master.blit(text, (self.cx - text_width // 2, self.cy - text_height // 2))
3 建立BubbleManager類管理氣泡
建立BubbleManager類管理氣泡,並進行冒泡排序控制
class BubbleManager:
def __init__(self, master, x, height, arr=[]):
self._master = master # 父控件窗體
self.bubblelist = [] # bubble列表
# 數組
if arr:
self.arr = arr
else:
self.arr = [i for i in range(1, NUMBER)]
random.shuffle(self.arr)
# 根據數組中的數字建立Bubble對象,添加到bubblelist中
# 注意動畫界面是從下往上展示數組的元素的
top_h = 0 # 上一個氣泡最高點到底部的距離(即圓心到底的距離加上氣泡半徑),初始爲0
for i in range(NUMBER-1):
v = self.arr[i]
vr = INIT_R + v * DR # 數字對應的半徑大小
center_h = top_h + BUBBLE_SPACE + vr # 當前氣泡圓心到界面底部的距離
top_h = center_h + vr # 記錄當前氣泡最高點到底部的距離,供下一個使用
bubble_i = Bubble(master, x, WIN_HEIGHT - center_h, v, vr)
self.bubblelist.append(bubble_i)
# 用於記錄冒泡排序過程的值
self.i = 0
self.j = 0
# 記錄當前氣泡j是否正在上浮
self.bubbling = False
# 控制動畫播放速度
self.count = 0
def draw(self):
# 繪製所有氣泡
for j in range(len(self.bubblelist)):
db = self.bubblelist[j]
db.draw(j==self.j)
def bubble_done(self):
# 檢查冒泡排序是否完成
return self.i >= NUMBER - 1
def bubble_j(self):
# 氣泡j上浮動畫
if not self.bubbling:
return
dbj0 = self.bubblelist[self.j]
dbj1 = self.bubblelist[self.j+1]
if dbj0.cy - dbj0.radius > dbj1.cy + dbj1.radius:
# j氣泡在下
dbj0.up()
dbj1.down()
elif dbj0.cy > dbj1.cy:
# 氣泡相交
cy0 = dbj0.cy
dbj0.cy = dbj1.cy - dbj1.radius + dbj0.radius
dbj1.cy = cy0 - dbj1.radius + dbj0.radius
elif (dbj1.cy - dbj1.radius) - (dbj0.cy + dbj0.radius) < BUBBLE_SPACE:
dbj0.up()
dbj1.down()
else:
self.bubblelist[self.j] = dbj1
self.bubblelist[self.j+1] = dbj0
self.bubbling = False
self.j += 1
return
def bubble_once(self):
# 進行一次冒泡排序
if self.j >= NUMBER - self.i - 2:
self.i += 1
self.j = 0
else:
if self.arr[self.j] > self.arr[self.j + 1]:
if self.count > 5:
self.count = 0
else:
self.count += 1
return
self.bubbling = True
self.arr[self.j], self.arr[self.j + 1] = self.arr[self.j + 1], self.arr[self.j]
self.bubble_j()
else:
if self.count > 10:
self.count = 0
self.j += 1
else:
self.count += 1
# 見註釋說明1-1
# def draw_pic(self):
# for j in range(len(self.bubblelist)):
# db = self.bubblelist[j]
# db.draw_pic(j==self.j)
4 實現可視化界面
這裏用一個Gui類去實現可視化
界面顯示後,點擊任意鍵盤按鍵即開始動畫
class Gui:
def __init__(self, width, height, count, fps=FPS, arr=[]):
self.win = pygame.display.set_mode((width, height))
self.clock = pygame.time.Clock()
self.fps = fps
pygame.display.set_caption("Bubble sort animation - made by Big Shuang")
self.bmlist = []
for i in range(1, count):
x = WIN_WIDTH // count * i
bm = BubbleManager(self.win, x, WIN_HEIGHT)
self.bmlist .append(bm)
self.start = False
def loop(self):
while True:
# 獲取所有事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
# 判斷當前事件是否爲點擊右上角退出鍵
pygame.quit()
if event.type == pygame.KEYDOWN:
# 點擊任意鍵開始動畫
self.start = True
if self.start:
for bm in self.bmlist :
if bm.bubbling:
bm.bubble_j()
elif not bm.bubble_done():
bm.bubble_once()
else:
bm.j = -1
self.win.fill(COLORS["bg"])
for bm in self.bmlist:
bm.draw()
self.clock.tick(self.fps)
pygame.display.update()
最後用如下調用一下就好
if __name__ == '__main__':
gui = Gui(WIN_WIDTH, WIN_HEIGHT, 10)
gui.loop()
運行後初始狀態截圖如下