Python寫春聯(turtle版)

位元組雜談發表於2022-01-30

Python就好比程式設計界的瑞士軍刀,開箱即用、無所不能。這得益於Python簡潔易用的語法,以及豐富的第三方庫,你想在電腦上做什麼,總能找到事半功倍的第三方庫。比如,在這新春佳節之際,用Python來寫個春聯能做到嗎?用Python自帶的turtle庫便可以實現。

turtle寫春聯
turtle寫春聯

一、turtle庫簡介

turtle中的繪圖是控制一隻“小海龜”來實現的,海龜移動的路徑便能形成圖形。這種程式繪圖的方式具有很強的可計算性,也涉及到許多數學幾何知識。

turtle的海龜繪圖很適合用來引導孩子學習程式設計。 最初來自於 Wally Feurzeig, Seymour Papert 和 Cynthia Solomon 於 1967 年所創造的 Logo 程式語言。

二、畫春聯背景

春聯背景的繪製比較簡單,由於上下聯、橫批都是矩形,且顏色都是一致的。為了便於程式碼的複用,可以定義可以一個畫背景的函式。如下所示:

def drawChunLianBg(startX=0, startY=0, lenX=100, lenY=100):
    # 設定邊框色和背景填充色
    color('Yellow', 'OrangeRed')
    # 抬筆,定位起點
    pu()
    goto(startX, startY)
    # 落筆,繪製春聯矩形框,並填充顏色
    pd()
    begin_fill()
    for i in range(2):
        fd(lenX)
        rt(90)
        fd(lenY)
        rt(90)
    end_fill()
    # 結束後抬筆
    pu()

三、寫字

方法一:write()函式直接寫字

關於寫字,turtle提供了一個write()函式。

使用write()函式列印文字效果
使用write()函式列印文字效果

優點:可以自定義漢字字型,寫出漂亮的字型!

缺點:無法呈現書寫字的動態效果。

turtle.write(arg, move=False, align='left', font=('Arial', 8, 'normal'))
  • arg -- 要書寫到 TurtleScreen 的物件

  • move -- True/False。 move 為真值,畫筆會移至文字的右下角

  • align -- 字串 "left", "center" 或 "right"。寫到當前海龜位置。

  • font -- 一個三元組 (fontname, fontsize, fonttype)

以下是實現關鍵程式碼:

def writeWord(target_word, startx, starty):
    # 基於座標,列印單個漢字
    color("black", "black")
    # 抬筆定位
    pu()
    goto(startx,starty)
    pd()
    # 基於字型,列印漢字
    write(target_word, move=False,align='left',font=('漢儀程行簡',24, 'normal'))

def writeWords(target_words, startx, starty,lineNum=1):
    # 列印多個漢字,lineNum控制每行的漢字數,預設為1
    # 向右、向下的偏移量
    right_shift = 0
    down_shift = 0
    # 遍歷列印漢字
    for word in target_words:
        writeWord(word, startx+right_shift*45, starty-down_shift*45)
        right_shift += 1
        # 判斷是否要換行
        if right_shift % lineNum == 0:
            down_shift += 1
            right_shift = 0

main()函式裡直接呼叫drawChunLianBgwriteWords()就可以畫出春聯和寫字啦。

def main():
    initScreen()
    drawChunLianBg(-160, 215, 60, 500)
    writeWords(target_words="一往無前 奔赴星辰大海", startx=-145, starty=175)
    drawChunLianBg(90, 215, 60, 500)
    writeWords(target_words="保持熱愛 靜待春暖花開", startx=105, starty=175)
    drawChunLianBg(-110, 280, 210, 60)
    writeWords(target_words="虎虎生威", startx=-90, starty=230,lineNum=4)
    pu()
    goto(0, 0)
    done()

方法二:利用座標“畫”漢字

如果要實現本文一開始的書寫效果,思路便是要將漢字當作圖形畫出來。即需要知道每個漢字筆畫中的關鍵座標,讓海龜根據座標來移動,畫出漢字。比如“萬”這個字,我們可以這樣來繪製:

座標畫“萬”字原理
座標畫“萬”字原理

也就是說,座標越多,畫出的漢字效果會越好。但可以想象這是一個非常繁瑣、耗時的工作。本想放棄的我,在知乎發現了一位大神也做過類似的嘗試,並且是利用Python爬蟲實現了從網上獲取中文字型筆畫座標。在本人感嘆其精妙的程式碼後,本著沒有必要重複造輪子的想法,本人最終決定copy核心程式碼試一試,沒想到竟然成功了!

基本原理是這樣的

爬蟲+turtle畫字
爬蟲+turtle畫字

關鍵程式碼:

def drawWords(target_words, startx, starty, lineNum=1):
    params = (
        ('font', gen_url_encode_words(target_words)),
        ('shi_fou_zi_dong', '0'),
        ('cache_sjs1', '20031908'),
    )
     # 發起網頁請求
    response = requests.get('https://bihua.51240.com/web_system/51240_com_www/system/file/bihua/get_0/',
                            headers=headers,
                            params=dict(params)
                            )

    content = response.content.decode('utf-8')
      # 解析每個字的筆畫座標
    content = content.replace(
        'hzbh.main(', '').split(');document.getElementById')[0]
    content = content.split('{')[-1].split("}")[0]
    pattern = re.compile(r'\w:\[(.+?)\]')
    result1 = re.split(pattern, content)
    words_order_list = {}
    words_cnt = 0
    for r in result1:
        sec = re.findall(r'\'.+?\'', r)
        if len(sec):
            orders = sec[1].split('#')
            order_xy_routine = []
            for order in orders:
                order_str = re.findall(r'\(\d+,\d+\)', order)
                order_xy = [eval(xy) for xy in order_str]
                order_xy_routine.append(order_xy)
            words_order_list['{}_{}'.format(
                words_cnt, target_words[words_cnt])] = order_xy_routine
            words_cnt += 1
    setting()  # 畫布、畫筆設定
    right_shift = 0
    down_shift = 0
    for k, v in words_order_list.items():
        for lines in v:
            pu()
            for xy in lines:
                x, y = xy
                x, y = x * 0.05 + startx + right_shift * 50, -y * 0.05 + starty - down_shift * 50
                print(x, y)
                goto(x, y)
                pd()
        right_shift += 1
        if right_shift % lineNum == 0:
            down_shift += 1
            right_shift = 0
turtle寫春聯
turtle寫春聯

寫字與畫字

利用write()寫字注重生成的結果,效果更好;而利用座標“畫”字注重過程,是爬蟲技術海龜畫圖的完美合作,更有意思,讓人不禁再次感嘆Python的NB,每每這時總會想起那句至理名言——“人生苦短,我用Python!”

寫在最後

本文是圍繞turtle庫來構思寫春聯這件事的,其實用Python寫春聯遠不止這一種方式。比如下面這副春聯也是利用Python寫的,簡直太酷了。

圖片庫生成的春聯

立個小小的flag,明年就來寫這樣的春聯吧。(望不打臉?)

最重要的話留在最後,祝大家虎年萬事順遂,保持熱愛,奔赴星海!

位元組雜談
位元組雜談

關注公眾號,回覆“春聯”,即可獲取原始碼

相關文章