1. 程式人生 > >Wolf從零學程式設計-用Python打造簡單加密程式(八)

Wolf從零學程式設計-用Python打造簡單加密程式(八)

本篇是最後一篇,加密小程式已經做完了,也可以打包帶走。

這篇列出了幾個改進,我的目的是:
- 儘量改善使用者體驗
- DEBUG

一、Fix the Bugs

函式引數順序不同導致BUG

之前寫完太激動,昨天耐著性子把所有功能走了一遍(原諒我,還不會寫測試),結果是這樣的:

  • 生成金鑰正常
  • DES加解密正常
  • RSA加解密正常
  • 混合模式讀不出金鑰,報錯資訊如下:
  File "E:\crypto\crypto\base.py", line 44, in getPubKey
    pub_key = rsa.PublicKey.load_pkcs1(keydata)
  File "C:\Python
34\lib\site-packages\rsa\key.py", line 75, in load_pkcs1 return method(keyfile) File "C:\Python34\lib\site-packages\rsa\key.py", line 243, in _load_pkcs1_pem der = rsa.pem.load_pem(keyfile, 'RSA PUBLIC KEY') File "C:\Python34\lib\site-packages\rsa\pem.py", line 91, in load_pem raise ValueError('No PEM start marker "%s" found' % pem_start)
ValueError: No PEM start marker "b'-----BEGIN RSA PUBLIC KEY-----'" found
  • 數字簽名簽名、正反驗證均正常

為此我同時把混合模式、RSA,以及被呼叫的base都開啟,一行一行的比對,結論是混合模式和RSA對應的呼叫部分完全相同,不可能出錯。我又想到具體的方法呼叫發生在crypto.py裡,一併開啟重新看程式碼,發現以下不同:

# crypto.py
mymix.encMix(rawfilename,key_filename,mode,operation)

# mymix.py
def encMix(key_filename,rawfilename,mode,operation)
:
# myrsa.py def encFile(rawfilename,key_filename,mode,operation):

唯一的區別就是:encMix()的引數名位置和其他兩個不同,當crypto中呼叫時,會把原始檔的路徑傳給金鑰檔案,造成無法讀取。我自己編寫時,都是每個函式單獨測試,檔案路徑是input的,單個模組內使用引數順序都是一樣的,所以沒發現問題。

果然,修改引數順序後,執行正常。到這裡整個程式就可以完全運行了。

操作成功後焦點停在DES初始值

測試時常用DES加密,發現操作成功後雖然輸入框都清空了(本篇後面的一處改進),但滑鼠焦點卻在DES初始值處,這時除非使用者輸入8位元組,否則必然會出現一次彈窗。

查詢發現,在DES模式中,DES初始值是最後一個使用焦點輸入的內容,步驟最後的原始檔通常都是通過瀏覽檔案選擇的。

之前有想過讓每個輸入框預設為’disabled’,待選擇模式後再判斷啟用,這樣也好看,剛巧也可以解決這個BUG。

# ginterface.py

# 元件預設禁用狀態
des_key_entry = Entry(key_frame,**state='disabled'**,textvariable=des_key)

#執行按鈕呼叫方法
def cryption():
    ...
    #清空輸入框
    if textVar == '操作完成':
        mode_choice.set('')
        operation_choice.set('')
        des_key_entry.delete(0,END)
        des_key_entry['state'] = 'disabled'  #清空後恢復禁用狀態

二、Improvement

DES金鑰有效性驗證

DES金鑰和初始值要求必須是8個位元組,entry元件的驗證功能就可以完成,沒必要使用事件繫結。

對於entry元件的內容驗證,可以看看魚C的講解,很詳細

我希望當用戶把焦點離開輸入框時觸發驗證,如果不足8位元組則清空輸入框,並彈出提示框;為了清空輸入框,就必須呼叫delete()方法,所以沒辦法讓DES金鑰和初始值驗證使用同一個方法;彈出提示框可以用tkinter的標準對話方塊messagebox

# ginterface.py

# 驗證DES金鑰和初始值合法性
def valiDeskey():
    if len(des_key.get()) == 8:
        return True
    else:
        des_key_entry.delete(0,END)  # 不是8位元組就直接清空
        messagebox.showerror('輸入錯誤','請輸入8位元組DES金鑰',default='ok',icon='warning')
        return False   # 驗證函式的返回必須是True或False

# DES金鑰
...
des_key_entry = Entry(key_frame,textvariable=des_key,validate='focusout',validatecommand=valiDeskey,width=10,show='*')
...

DES初始值的驗證完全一樣,只是把相應的函式名和變數名更改。

目前這種寫法要求使用者一旦選擇輸入DES金鑰,就要確保輸入8個位元組,中途做任何其他的事情都會使輸入框失去焦點,即彈出提示。

改進模式選擇

目前的邏輯是:使用者選擇模式和操作,當焦點離開時,會根據已選模式和操作,判斷金鑰區哪一個輸入框可用,但是我設定了預設值是DES和加密,使用者有可能壓根就不會把焦點放在模式選擇框,因此不會對輸入框狀態進行更改,這種情況我是不允許的!

把模式和操作設定預設值的程式碼刪掉,妥妥的逼使用者選擇。

操作成功後清空輸入

使用者很可能在操作一次DES後緊接著開始一次RSA操作,這時應該讓軟體介面恢復到剛開啟的狀態,也就是把所有的輸入框都清空。我把清空操作新增在了’執行操作’的方法裡,且只有當用戶操作成功後才會清空。

# ginterface.py

def cryption():
    ...
    #清空輸入框
    if textVar == '操作完成':
        mode_choice.set('')
        operation_choice.set('')
        des_key_entry.delete(0,END)  # Entry元件沒有set方法
        des_IV_entry.delete(0,END)
        key_filename_entry.delete(0,END)
        sig_filename_entry.delete(0,END)
        rawfilename_entry.delete(0,END)

新增對話方塊清空功能

當用戶覺得對話方塊內容過多時,可以一鍵清空。同時為了避免使用者在對話方塊隨意輸入內容,我把對話方塊的預設state設為disabled,只有當列印操作進度或清空時短暫恢復normal

# ginterface.py

text = Text(height=10,width=60,bd=3,relief=SUNKEN,wrap=WORD,state='disabled') 
#高度、寬度、邊框寬度和樣式、按單詞換行
text.pack(padx=1,pady=5)
def empty():
    text['state'] = 'normal'
    text.delete(1.0,END)  #1.0是起始位置
    text['state'] = 'disabled'
empty_button = Button(text='清空對話方塊',command=empty)
empty_button.pack(padx=20,pady=5,side=RIGHT)

捕獲錯誤資訊

為了讓使用者隨心輸入隨時可以點選’執行操作’按鈕,需要把程式碼執行中的錯誤資訊捕獲,並且對於使用者選擇的錯誤模式給出提示。

將原先的

# ginterface.py

def cryption():
    ...
    textVar = crypto.doCrypto(mode,operation,des_key,des_IV,key_filename,sig_filename,rawfilename)
    text['state'] = 'normal'
    text.insert(END,textVar)
    text.insert(END,'\n')
    text['state'] = 'disabled'

改為

def cryption():
    ...
    try:
        textVar = crypto.doCrypto(mode,operation,des_key,des_IV,key_filename,sig_filename,rawfilename)
    except:
        textVar = '請正確輸入各項引數'
    text['state'] = 'normal'
    text.insert(END,textVar)
    text.insert(END,'\n')
    text['state'] = 'disabled'

然後在’crypto.py’中,根據使用者的選擇給返回值賦值,例如使用者選擇了DES-簽名,就返回’請選擇正確的操作’,測試結果如圖:

mark

給進度對話方塊安裝垂直滾動條

tkinter的Scrollbar元件可以給Text元件安裝垂直和水平滾動條,我需要做的事情有:
- 新建一個Frame元件,作為對話方塊和滾動條的父元件
- 設定Text元件的yscrollcommand選項為Scrollbar元件的set()
- 設定Scrollbar元件的command選項為Text元件的yview()

#ginterface.py

#對話方塊框架,此框架作用是方便排版
text_frame = Frame(root)
text_frame.pack()

#滾動條和對話方塊
textbar = Scrollbar(text_frame,takefocus=False)  #滾動條不需要焦點
textbar.pack(side=RIGHT,fill=Y)
text = Text(text_frame,height=10,width=60,bd=3,yscrollcommand=textbar.set,relief=SUNKEN,wrap=WORD,state='disabled')
#高度、寬度、邊框寬度和樣式、按單詞換行
text.pack(side=RIGHT,fill=BOTH)
textbar['command'] = text.yview  #yview是Text元件自帶方法

清空無效輸入框

使用者可能會先選擇DES模式,輸入金鑰後發現選錯了,應該是RSA模式,這時候肯定是直接更改模式,已經輸入的DES金鑰需要在禁用前自動清空。

只需要在模式判斷里加一行清空就可以,不過只有需要禁用的輸入框新增這一行。

# ginterface.py

def judgMode(event):
    if mode.get() == 'DES':
        des_key_entry['state'] = 'normal'
        des_IV_entry['state'] = 'normal'
        **key_filename_entry.delete(0,END)**
        **sig_filename_entry.delete(0,END)**
        key_filename_entry['state'] = 'disabled'
        sig_filename_entry['state'] = 'disabled'

目前為止這個小軟體就算完工了,還有一些考慮新增的功能暫時想不到辦法,比如:

  • 只允許通過瀏覽檔案選擇檔案,避免使用者輸入的錯誤
  • 進度對話方塊實時列印程式碼執行進度和操作耗時

醜陋的程式碼也需要不斷優化,總之我會邊學習邊進行,github倉庫會即時更新。