#_*_ coding:utf8 _*_
## Python3 密碼管理 V1.0
## 2020-05-28
## 使用密鑰對加密解密密碼,密文存入本地SQLite3數據庫,使用時直接右鍵解密到粘貼板中
import os # 操作Windows文件用
import time
import random
#import hashlib
import rsa
## 數據庫
import sqlite3
## 圖形化模塊
from tkinter import *
from tkinter import filedialog # 選擇文件用
import tkinter.messagebox # 彈出提示對話框
from tkinter import ttk # 下拉菜單控件在ttk中
top = Tk() # 初始化Tk()
top.title('Python3 密碼管理 V1.0') # 設置標題
width = 800 # 設置窗口寬
height = 700 # 設置窗口高
# 獲取屏幕尺寸以計算佈局參數,使窗口居屏幕中央
screenwidth = top.winfo_screenwidth()
screenheight = top.winfo_screenheight()
alignstr = '%dx%d+%d+%d' % (width, height, (screenwidth-width)/2, (screenheight-height)/2)
top.geometry(alignstr)
top.resizable(width=True, height=True) # 設置窗口是否可變長、寬,True:可變,False:不可變
######## Sqlite3 SQL 語句函數
##
## 數據庫操作:執行SQL查詢語句,返回執行狀態和執行結果(數據列表)
def DEF_SQL_查詢和返回(SQL_CMD):
數據庫連接對象 = sqlite3.connect('PWD.DB') # 嘗試打開數據庫文件
print("打開連接")
try:
遊標對象 = 數據庫連接對象.cursor() # 創建一個遊標
except Exception as e:
ERROR = '創建遊標失敗' + str(e)
return(1, ERROR)
else:
print("創建遊標")
try:
遊標對象.execute(SQL_CMD)
except Exception as e:
ERROR = f'執行SQL語句 {SQL_CMD} 失敗 {e}'
遊標對象.close()
print("關閉遊標")
return(1, ERROR)
else:
全部記錄 = 遊標對象.fetchall()
遊標對象.close()
print("關閉遊標")
return(0, 全部記錄)
## 執行一條SQL語句
def DEF_SQL_執行(SQL_CMD):
數據庫連接對象 = sqlite3.connect('PWD.DB') # 嘗試打開數據庫文件
print("打開連接")
try:
遊標對象 = 數據庫連接對象.cursor() # 創建一個遊標
except Exception as e:
ERROR = '創建遊標失敗' + str(e)
return(1, ERROR)
else:
print("創建遊標")
try:
遊標對象.execute(SQL_CMD)
except Exception as e:
遊標對象.close()
print("關閉遊標")
ERROR = str(e)
return(1, ERROR)
else:
數據庫連接對象.commit() # 提交更改
遊標對象.close()
print("關閉遊標")
數據庫連接對象.close()
print("關閉連接")
return(0,)
## 執行SQL腳本
def DEF_SQL_執行腳本(SQL_SCRIPT):
數據庫連接對象 = sqlite3.connect('PWD.DB') # 嘗試打開數據庫文件
print("打開連接")
try:
遊標對象 = 數據庫連接對象.cursor()
except Exception as e:
ERROR = str(e)
return(1, ERROR)
else:
print("創建遊標")
try:
遊標對象.executescript(SQL_SCRIPT) # 執行SQL腳本
except Exception as e:
遊標對象.close()
print("關閉遊標")
ERROR = str(e)
return(1, ERROR)
else:
數據庫連接對象.commit() # 提交更改
遊標對象.close()
print("關閉遊標")
數據庫連接對象.close()
print("關閉連接")
return(0,)
## 打開刷新數據表,PWD表不存在則新建
def 打開數據庫():
## 打開數據庫
## 讀取數據庫內容
SQL_CMD = 'SELECT * FROM PWD'
R = DEF_SQL_查詢和返回(SQL_CMD)
if R[0] == 0:
print("數據庫表PWD讀取成功")
數據列表 = R[1]
#print("數據列表", 數據列表)
字段列表 = ['ID', 'PS', 'USER', 'PASS']
字段和數據的存儲和展示(字段列表, 數據列表)
else:
## 創建表
print("數據庫表PWD讀取失敗,嘗試創建據庫表PWD")
SQL_CMD = '''CREATE TABLE IF NOT EXISTS PWD(
ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
PS TEXT,
USER TEXT,
PASS TEXT);'''
R = DEF_SQL_執行(SQL_CMD)
if R[0] == 0:
print("創建表PWD成功")
else:
ERROR = f'創建表失敗:{R[1]}'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
##
########
######## 新密鑰對操作框
##
def 生成新密鑰對():
當前設置密鑰長度 = IV_密鑰長度.get()
## 生成新密鑰對
(公鑰, 私鑰) = rsa.newkeys(當前設置密鑰長度)
## 保存新密鑰對
FILE_DATE = time.strftime('%Y%m%d_%H%M%S')
新公鑰文件名 = f'公鑰_{當前設置密鑰長度}_{FILE_DATE}.pem'
with open(新公鑰文件名, 'w') as f:
f.write(公鑰.save_pkcs1().decode())
新私鑰文件名 = f'私鑰_{當前設置密鑰長度}_{FILE_DATE}.pem'
with open(新私鑰文件名, 'w') as f:
f.write(私鑰.save_pkcs1().decode())
INFO = '生成新密鑰對完成,長度爲:' + str(當前設置密鑰長度)
SV_新密鑰對提示信息.set(INFO)
## 自動替換爲新生成密鑰對
SV_選擇公鑰文件.set(新公鑰文件名)
Label_公鑰提示信息['fg'] = 'green'
SV_公鑰提示信息.set('自動替換爲新生成的公鑰文件')
SV_選擇私鑰文件.set(新私鑰文件名)
Label_私鑰提示信息['fg'] = 'green'
SV_私鑰提示信息.set('自動替換爲新生成的私鑰文件')
新密鑰對操作框 = LabelFrame(top, text='新密鑰對操作框')
IV_密鑰長度 = IntVar()
IV_密鑰長度.set(1024) # 設置默認值 1024
SV_新密鑰對提示信息 = StringVar()
SV_新密鑰對提示信息.set('生成密鑰需要等待數秒時間')
Label(新密鑰對操作框, text='設置新密鑰長度').grid(row=0, column=0, sticky='NW')
#Entry(新密鑰對操作框, textvariable=IV_密鑰長度, width=5).grid(row=0, column=1)
Combobox_密鑰長度 = ttk.Combobox(新密鑰對操作框, width=5)
Combobox_密鑰長度['value'] = (1024, 2048, 3072, 4096)
Combobox_密鑰長度.current(0) # 默認值中的內容爲索引,從0開始
Combobox_密鑰長度.grid(row=0, column=1, sticky='E')
def 選擇後執行函數(event):
設置新密鑰長度 = Combobox_密鑰長度.get()
IV_密鑰長度.set(設置新密鑰長度)
print("設置新密鑰長度", 設置新密鑰長度)
Combobox_密鑰長度.bind('<<ComboboxSelected>>', 選擇後執行函數)
Button(新密鑰對操作框, text='生成新密鑰對', command=生成新密鑰對).grid(row=0, column=2, sticky='NW')
Label_新密鑰對提示信息 = Label(新密鑰對操作框, textvariable=SV_新密鑰對提示信息)
Label_新密鑰對提示信息['fg'] = '#FF6600'
Label_新密鑰對提示信息.grid(row=0, column=3)
新密鑰對操作框.grid(row=0, column=0, sticky='NW')
##
########
######## 公鑰文件框
##
SV_選擇公鑰文件 = StringVar() ## TK全局變量:可以實時更新顯示
SV_公鑰提示信息 = StringVar() ## 顯示公鑰文件的情況
def DEF_按鈕_選擇公鑰文件():
選擇文件 = filedialog.askopenfilename()
SV_選擇公鑰文件.set(選擇文件)
print("SV_選擇公鑰文件", SV_選擇公鑰文件)
## 選擇文件後更新一下提示信息
Label_公鑰提示信息['fg'] = 'green'
SV_公鑰提示信息.set('已手動選擇公鑰文件')
公鑰操作框 = LabelFrame(top, text='公鑰')
Button(公鑰操作框, text='選擇公鑰文件', command=DEF_按鈕_選擇公鑰文件).grid(row=0, column=0, sticky='NW')
Entry(公鑰操作框, textvariable=SV_選擇公鑰文件, width=55).grid(row=0, column=1)
Label_公鑰提示信息 = Label(公鑰操作框, textvariable=SV_公鑰提示信息)
Label_公鑰提示信息.grid(row=0, column=2)
默認公鑰文件 = '公鑰.pem'
if os.path.isfile(默認公鑰文件):
SV_選擇公鑰文件.set(默認公鑰文件) ## 設置公鑰默認存放位置爲程序同目錄下,方便使用
Label_公鑰提示信息['fg'] = 'green'
SV_公鑰提示信息.set('自動選擇默認公鑰文件')
else:
Label_公鑰提示信息['fg'] = '#FF6600'
SV_公鑰提示信息.set('加密需要先選擇公鑰')
公鑰操作框.grid(row=1, column=0, sticky='NW')
##
########
######## 私鑰文件框
##
SV_選擇私鑰文件 = StringVar()
SV_私鑰提示信息 = StringVar()
def DEF_按鈕_選擇私鑰文件():
選擇文件 = filedialog.askopenfilename()
SV_選擇私鑰文件.set(選擇文件) # 實時更新顯示
print("SV_選擇私鑰文件", SV_選擇私鑰文件)
Label_私鑰提示信息['fg'] = 'green'
SV_私鑰提示信息.set('已手動選擇私鑰文件')
私鑰操作框 = LabelFrame(top, text='私鑰')
Button(私鑰操作框, text='選擇私鑰文件', command=DEF_按鈕_選擇私鑰文件).grid(row=0, column=0, sticky='NW')
Entry(私鑰操作框, textvariable=SV_選擇私鑰文件, width=55).grid(row=0, column=1)
Label_私鑰提示信息 = Label(私鑰操作框, textvariable=SV_私鑰提示信息)
Label_私鑰提示信息.grid(row=0, column=2)
默認私鑰文件 = '私鑰.pem'
if os.path.isfile(默認私鑰文件):
SV_選擇私鑰文件.set(默認私鑰文件) ## 設置私鑰默認存放位置爲程序同目錄下,方便使用(不安全,私鑰加密後可以提高安全性)
Label_私鑰提示信息['fg'] = 'green'
SV_私鑰提示信息.set('自動選擇默認私鑰文件')
else:
Label_私鑰提示信息['fg'] = '#FF6600'
SV_私鑰提示信息.set('提示:解密需要先選擇私鑰')
私鑰操作框.grid(row=2, column=0, sticky='NW')
##
########
######## 存儲操作框
##
def 明文存入():
print("明文存入")
備註 = Entry_備註.get().strip()
帳號 = Entry_帳號.get().strip()
密碼 = Entry_密碼.get().strip()
if 備註 == '' or 帳號 == '' or 密碼 == '':
ERROR = '備註/帳號/密碼 中有空內容'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
else:
#print(備註,帳號,密碼)
## 存入數據庫
SQL_插入數據 = f'INSERT INTO PWD (PS, USER, PASS) VALUES ("{備註}", "{帳號}", "{密碼}");'
R_INSERT = DEF_SQL_執行(SQL_插入數據)
if R_INSERT[0] == 0:
print("插入數據成功")
Entry_備註.delete(0, END) # 界面:清空輸入框
Entry_帳號.delete(0, END) # 界面:清空輸入框
Entry_密碼.delete(0, END) # 界面:清空輸入框
打開數據庫()
else:
#print("插入數據失敗", R_INSERT[1])
ERROR = f'插入數據失敗:{R_INSERT[1]}'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
def 密文存入():
print("密文存入")
備註 = Entry_備註.get().strip()
帳號 = Entry_帳號.get().strip()
密碼 = Entry_密碼.get().strip()
if 備註 == '' or 帳號 == '' or 密碼 == '':
ERROR = '備註/帳號/密碼 中有空內容'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
else:
#print(備註,帳號,密碼)
## 明文密碼加密
公鑰文件 = SV_選擇公鑰文件.get()
if 公鑰文件.rstrip() == '':
ERROR = '沒有選擇公鑰文件'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
else:
try:
f = open(公鑰文件,'r')
except Exception as E:
print("打開公鑰文件失敗", E)
ERROR = f'打開公鑰文件失敗:{E}'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
else:
print("打開公鑰文件成功")
try:
pubkey = rsa.PublicKey.load_pkcs1(f.read().encode())
except Exception as E:
print("讀取公鑰失敗", E)
f.close()
ERROR = f'讀取公鑰失敗:{E}'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
else:
print("讀取公鑰成功")
f.close()
密碼_B = str(密碼).encode('UTF8') # 轉成UTF8編碼的字節
#print("文本密碼轉字節密碼", 密碼_B, type(密碼_B))
## 公鑰加密
公鑰加密後密文 = rsa.encrypt(密碼_B, pubkey)
#print("公鑰加密後密文", 公鑰加密後密文)
## 字節類型轉成16進制字符串存儲到數據庫
STR_HEX = Bytes2HEX_STR(公鑰加密後密文)
#print("公鑰加密後密文 轉 STR_HEX", type(STR_HEX))
#print(STR_HEX)
pubkey = '' # 每次用完清空
## 存入數據庫
SQL_插入數據 = f'INSERT INTO PWD (PS, USER, PASS) VALUES ("{備註}", "{帳號}", "{STR_HEX}");'
R_INSERT = DEF_SQL_執行(SQL_插入數據)
if R_INSERT[0] == 0:
print("插入數據成功")
Entry_備註.delete(0, END) # 界面:清空輸入框
Entry_帳號.delete(0, END) # 界面:清空輸入框
Entry_密碼.delete(0, END) # 界面:清空輸入框
打開數據庫() # 更新數據庫顯示框
else:
ERROR = f'插入數據失敗:{R_INSERT[1]}'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
def 帳號密碼加密存入():
print("帳號和密碼都加密存儲")
備註 = Entry_備註.get().strip()
帳號 = Entry_帳號.get().strip()
密碼 = Entry_密碼.get().strip()
if 備註 == '' or 帳號 == '' or 密碼 == '':
ERROR = '備註/帳號/密碼 中有空內容'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
else:
#print(備註,帳號,密碼)
## 明文密碼加密
公鑰文件 = SV_選擇公鑰文件.get()
if 公鑰文件.rstrip() == '':
ERROR = '沒有選擇公鑰文件'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
else:
try:
f = open(公鑰文件,'r')
except Exception as E:
print("打開公鑰文件失敗", E)
ERROR = f'打開公鑰文件失敗:{E}'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
else:
print("打開公鑰文件成功")
try:
pubkey = rsa.PublicKey.load_pkcs1(f.read().encode())
except Exception as E:
print("讀取公鑰失敗", E)
f.close()
ERROR = f'讀取公鑰失敗:{E}'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
else:
print("讀取公鑰成功")
f.close()
## 公鑰加密密碼
密碼_B = str(密碼).encode('UTF8') # 轉成UTF8編碼的字節
密碼公鑰加密後密文 = rsa.encrypt(密碼_B, pubkey)
STR_HEX_密碼 = Bytes2HEX_STR(密碼公鑰加密後密文) ## 字節類型轉成16進制字符串
## 公鑰加密帳號
帳號_B = str(帳號).encode('UTF8')
帳號公鑰加密後密文 = rsa.encrypt(帳號_B, pubkey)
STR_HEX_帳號 = Bytes2HEX_STR(帳號公鑰加密後密文)
pubkey = '' # 每次用完清空
## 存入數據庫
SQL_插入數據 = f'INSERT INTO PWD (PS, USER, PASS) VALUES ("{備註}", "{STR_HEX_帳號}", "{STR_HEX_密碼}");'
R_INSERT = DEF_SQL_執行(SQL_插入數據)
if R_INSERT[0] == 0:
print("插入數據成功")
Entry_備註.delete(0, END) # 界面:清空輸入框
Entry_帳號.delete(0, END) # 界面:清空輸入框
Entry_密碼.delete(0, END) # 界面:清空輸入框
打開數據庫() # 更新數據庫顯示框
else:
ERROR = f'插入數據失敗:{R_INSERT[1]}'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
def 搜索備註信息():
print("搜索備註信息")
備註 = Entry_備註.get().strip()
if 備註 == '':
ERROR = '備註空內容'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
else:
print("搜索備註", 備註)
SQL_CMD = f'SELECT * FROM PWD WHERE PS LIKE "%{備註}%"'
print("SQL_CMD", SQL_CMD)
R = DEF_SQL_查詢和返回(SQL_CMD)
if R[0] == 0:
print("執行搜索語句成功")
數據列表 = R[1]
#print("數據列表", 數據列表)
字段列表 = ['ID', 'PS', 'USER', 'PASS']
字段和數據的存儲和展示(字段列表, 數據列表)
else:
ERROR = f'執行搜索語句失敗:{R[1]}'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
存儲操作框 = LabelFrame(top, text='存儲操作框')
存儲操作框.grid(row=3, column=0, sticky='NW') ## top 3-1
備註框 = Frame(存儲操作框)
備註框.grid(row=1, column=0, sticky='NW') ## 存儲操作框 1-0
Label(備註框, text='備註').grid(row=0, column=0) ## 存儲操作框 備註框 0-0
Entry_備註 = Entry(備註框, width=20)
Entry_備註.grid(row=1, column=0) ## 存儲操作框 備註框 1-0
Button(備註框, text='搜索備註信息', command=搜索備註信息).grid(row=2, column=0) ## 存儲操作框 備註框 2-0
帳號框 = Frame(存儲操作框)
帳號框.grid(row=1, column=1, sticky='NW') ## 存儲操作框 1-1
Label(帳號框, text='帳號').grid(row=0, column=0) ## 存儲操作框 帳號框 0-0
Entry_帳號 = Entry(帳號框, width=20)
Entry_帳號.grid(row=1, column=0) ## 存儲操作框 帳號框 1-0
密碼框 = Frame(存儲操作框)
密碼框.grid(row=1, column=2, sticky='NW') ## 存儲操作框 1-2
Label(密碼框, text='密碼').grid(row=0, column=0) ## 存儲操作框 密碼框 0-0
Entry_密碼 = Entry(密碼框, width=30)
Entry_密碼.grid(row=1, column=0) ## 存儲操作框 密碼框 1-0
數據庫寫入框 = Frame(存儲操作框)
數據庫寫入框.grid(row=1, column=3, sticky='ESWN') ## 存儲操作框 1-3
Label(數據庫寫入框, text='密碼存入數據庫方式').grid(row=0, column=0, columnspan=3) ## 存儲操作框 數據庫寫入框 0-0
Button(數據庫寫入框, text=' 明文存入 ', command=明文存入).grid(row=1, column=0, sticky='NW') ## 存儲操作框 數據庫寫入框 1-0
Button(數據庫寫入框, text=' 密文存入 ', command=密文存入).grid(row=1, column=1, sticky='NW') ## 存儲操作框 數據庫寫入框 1-1
Button(數據庫寫入框, text=' 帳號和密碼都加密 ', command=帳號密碼加密存入).grid(row=1, column=2, sticky='NW') ## 存儲操作框 數據庫寫入框 1-2
SV_剪貼板提示信息 = StringVar()
Label_剪貼板提示信息 = Label(存儲操作框, textvariable=SV_剪貼板提示信息)
Label_剪貼板提示信息['fg'] = '#FF6600'
Label_剪貼板提示信息.grid(row=2, column=0, columnspan=3, sticky='ESWN') ## 存儲操作框 2-0
##
########
######## 密碼操作框
##
def 生成隨機密碼():
密碼長度 = IV_密碼位數.get()
數字字符列表 = ['0','1','2','3','4','5','6','7','8','9']
大寫字母列表 = ['A','B','C','D','E','F','G','H','J','K','M','N','P','Q','R','S','T','U','V','W','X','Y','Z']
小寫字母列表 = ['a','b','c','d','e','f','g','h','j','k','m','n','p','q','r','s','t','u','v','w','x','y','z']
符號字符列表 = ['`','~','!','@','#','$','%','^','&','*','(',')','-','_','=','+','[','{',']','}','|',';',':','<','.','>','/','?']
組合列表 = 數字字符列表 + 大寫字母列表 + 小寫字母列表 + 符號字符列表
if 密碼長度 > 84:
ERROR = '過長,請設置長度 <= 84'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
else:
random.shuffle(組合列表)
隨機密碼 = ''
for i in range(0, 密碼長度):
隨機密碼 += 組合列表[i]
#print(隨機密碼)
Entry_密碼.delete(0, END) # 界面:清空輸入框
Entry_密碼.insert(END, 隨機密碼)
## 字節碼轉16進制格式的字符串
def Bytes2HEX_STR(Bytes):
STR_HEX = ''
for i in Bytes:
STR_HEX += hex(i)[2:].zfill(2) ## '0x50' 取 '50' 部分,如果不足2個字符,填充0
return(STR_HEX)
IV_密碼位數 = IntVar()
IV_密碼位數.set(20) ## 設置默認密碼位數
密碼操作框 = LabelFrame(存儲操作框, text='')
Label(密碼操作框, text='設置密碼長度').grid(row=0, column=0, sticky='NW')
Entry(密碼操作框, textvariable=IV_密碼位數, width=5).grid(row=0, column=1)
Button(密碼操作框, text='生成隨機密碼', command=生成隨機密碼).grid(row=0, column=2, sticky='NW')
Button(密碼操作框, text='刷新數據庫', bg='#00FFFF', command=打開數據庫).grid(row=0, column=3, sticky='NW')
密碼操作框.grid(row=0, column=0, sticky='NW', columnspan=8)
##
########
######## 數據庫顯示/操作框
##
字典_查詢字段_座標_初值 = {} # KEY=控件座標 || VAULE=初始值 || {(控件行號,控件列號):初始值} || { (0,0):123 }
字典_查詢結果_座標_初值 = {} # KEY=控件座標 || VAULE=初始值 || {(控件行號,控件列號):初始值} || { (0,0):123 }
數據框_定位行 = IntVar()
數據框_定位列 = IntVar()
## 信息複製到剪貼板
def DEF_解密後複製到剪貼板():
控件行號 = 數據框_定位行.get()
控件列號 = 數據框_定位列.get()
print("控件行號", 控件行號, "控件列號", 控件列號)
選中單元格內容 = 字典_查詢結果_座標_初值[(控件行號,控件列號)]
print("選中單元格內容", 選中單元格內容)
## 解密密文
私鑰文件 = SV_選擇私鑰文件.get()
if 私鑰文件.rstrip() == '':
ERROR = '沒有選擇私鑰文件'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
else:
try:
f = open(私鑰文件,'r')
except Exception as E:
ERROR = f'打開私鑰文件失敗:{E}'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
else:
print("打開私鑰文件成功")
try:
privkey = rsa.PrivateKey.load_pkcs1(f.read().encode())
except Exception as E:
print("讀取私鑰失敗", E)
f.close()
ERROR = f'讀取私鑰失敗:{E}'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
else:
print("讀取私鑰成功")
f.close()
## 讀取字符串並還原字節類型
try:
還原字節碼 = bytes.fromhex(選中單元格內容)
except Exception as E:
#print("還原字節碼失敗,格式有誤(非16進制字符串形式", E)
ERROR = f'還原字節碼失敗,格式有誤(非16進制字符串形式:{E}'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
else:
#print("還原字節碼", type(還原字節碼))
#print(還原字節碼)
## 私鑰解密
try:
B_私鑰解密後明文 = rsa.decrypt(還原字節碼, privkey)
except Exception as E:
#print("私鑰解密失敗", E)
ERROR = f'私鑰解密失敗:{E}'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
else:
print("私鑰解密成功")
#print("B_私鑰解密後明文(字節碼)", type(B_私鑰解密後明文))
S_私鑰解密後明文 = B_私鑰解密後明文.decode()
#print("S_私鑰解密後明文(字符串)", S_私鑰解密後明文)
privkey = '' # 每次用完清空
top.clipboard_clear()
top.clipboard_append(S_私鑰解密後明文)
## 提示哪個數據被複制到剪貼板
選中行ID = 字典_查詢結果_座標_初值[(控件行號,0)]
選中行列名 = 字典_查詢字段_座標_初值[(0,控件列號)]
INFO = f'ID【{選中行ID}】的【{選中行列名}】【解密後內容】 已經複製到剪貼板'
#tkinter.messagebox.showinfo(title='INFO', message=INFO)
SV_剪貼板提示信息.set(INFO)
def DEF_保留原值複製到剪貼板():
控件行號 = 數據框_定位行.get()
控件列號 = 數據框_定位列.get()
print("控件行號", 控件行號, "控件列號", 控件列號)
選中單元格內容 = 字典_查詢結果_座標_初值[(控件行號,控件列號)]
print("選中單元格內容", 選中單元格內容)
top.clipboard_clear()
top.clipboard_append(選中單元格內容)
## 提示哪個數據被複制到剪貼板
選中行ID = 字典_查詢結果_座標_初值[(控件行號,0)]
選中行列名 = 字典_查詢字段_座標_初值[(0,控件列號)]
INFO = f'ID【{選中行ID}】的【{選中行列名}】【原值】 已經複製到剪貼板'
#tkinter.messagebox.showinfo(title='INFO', message=INFO)
SV_剪貼板提示信息.set(INFO)
## 菜單按鈕函數:刪除整行(提取控件行號信息)
def DEL_ROW():
控件行號 = 數據框_定位行.get()
DEF_刪除記錄(控件行號)
## 刪除成功後的行列號和當前行列號有差別,立刻設置爲無效行列號,防止後面誤刪
數據框_定位行.set(-1)
數據框_定位列.set(-1)
## 菜單按鈕函數:刪除整行(根據控件行號刪除)
def DEF_刪除記錄(控件行號):
ID = 字典_查詢結果_座標_初值[(控件行號, 0)]
print("DELETE ID", ID)
SQL_CMD = f'DELETE FROM PWD WHERE ID = "{ID}"'
print("SQL_CMD", SQL_CMD)
## 刪除記錄
R = DEF_SQL_執行(SQL_CMD)
if R[0] == 0:
打開數據庫()
else:
ERROR = f'刪除記錄失敗:{R[1]}'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
## 事件函數:右鍵菜單
def DEF_彈出_數據框_右鍵菜單(event):
# 取值控制座標
選中控件 = event.widget
行 = 選中控件.grid_info()['row']
列 = 選中控件.grid_info()['column']
# 賦值控制座標變量
數據框_定位行.set(行)
數據框_定位列.set(列)
## 彈出菜單
光標X軸 = event.x_root
光標Y軸 = event.y_root
數據框_右鍵菜單.post(光標X軸, 光標Y軸) # 光標位置顯示菜單
## 創建數據框右鍵菜單
數據框_右鍵菜單 = Menu()
數據框_右鍵菜單.add_command(label='解密 到 剪貼板', command=DEF_解密後複製到剪貼板)
數據框_右鍵菜單.add_separator() # 分隔線
數據框_右鍵菜單.add_separator()
數據框_右鍵菜單.add_command(label='原值 到 剪貼板', command=DEF_保留原值複製到剪貼板)
數據框_右鍵菜單.add_separator()
數據框_右鍵菜單.add_separator()
數據框_右鍵菜單.add_command(label='刪除此行', command=DEL_ROW)
## 清空框內控件
def FRAME_CLEAR(FRAME_NAME):
for X in FRAME_NAME.winfo_children():
X.destroy()
## 在顯編框展示結果,保存結果到全局變量,可以進行修改操作
def 字段和數據的存儲和展示(L, LL):
行數 = len(LL)
FRAME_CLEAR(LabelFrame_顯編框) # 清空框內控件
## 創建畫布
畫布 = Canvas(LabelFrame_顯編框, bg='#00CED1') # 創建畫布
畫布.grid(row=0,column=0) # 顯示畫布
## 在畫布裏創建 Frame
畫布Frame框 = Frame(畫布)
字段框 = Frame(畫布Frame框)
字段框.grid(row=0,column=0,sticky='NW')
數據框 = Frame(畫布Frame框)
數據框.grid(row=1,column=0,sticky='NW')
## 動態設置畫布窗口寬高:根據主主窗口的參數設置限寬限高
主窗口大小和位置 = top.geometry()
主窗口寬, 主窗口高, 主窗口X, 主窗口Y = re.findall('[0-9]+', 主窗口大小和位置)
畫布限寬 = int(主窗口寬)
print("畫布限寬", 畫布限寬)
if 畫布限寬 < 589:
畫布限寬 = 589 # 保障最小寬度
畫布限高 = int(主窗口高) -300 # 減去顯示框上邊的內容
print("畫布限高", 畫布限高)
if 畫布限高 < 250:
畫布限高 = 250 # 保障最小高度
## 設置畫布參數
總行數 = 行數 + 1
## 畫布可滾動顯示的最大寬和高(要剛好能放下畫布裏的Frame裏的全部控件)
畫布滾動最右邊 = 786 ## Enter默認寬20==140像素(7像素/每寬) ID 10 PS 30 USER20 PASS50
畫布滾動最下邊 = 21*總行數 ## 20*行數 + 行數*1
## 動態設置顯示畫布固定顯示寬和高(要和主顯示框的大小匹配)
if 畫布限寬 > 畫布滾動最右邊:
畫布['width'] = 畫布滾動最右邊
else:
畫布['width'] = 畫布限寬 - 30
if 畫布限高 > 畫布滾動最下邊:
畫布['height'] = 畫布滾動最下邊
else:
畫布['height'] = 畫布限高
畫布['scrollregion'] = (0,0,畫布滾動最右邊,畫布滾動最下邊) # 一個元組 tuple (w, n, e, s) ,定義了畫布可滾動的最大區域,w 爲左邊,n 爲頭部,e 爲右邊,s 爲底部
# 豎滾動條
Scrollbar_畫布_豎 = Scrollbar(LabelFrame_顯編框, command=畫布.yview)
Scrollbar_畫布_豎.grid(row=0,column=1,sticky=S+W+E+N)
# 橫滾動條
Scrollbar_畫布_橫 = Scrollbar(LabelFrame_顯編框, command=畫布.xview, orient=HORIZONTAL)
Scrollbar_畫布_橫.grid(row=1,column=0,sticky=S+W+E+N)
畫布.config(xscrollcommand=Scrollbar_畫布_橫.set, yscrollcommand=Scrollbar_畫布_豎.set) # 自動設置滾動幅度
畫布.create_window((0,0), window=畫布Frame框, anchor='nw')
## 在 畫布裏的Frame裏創建控件
# 清除全局字典的內容
字典_查詢字段_座標_初值.clear()
字典_查詢結果_座標_初值.clear()
## 字段名
## 固定行:0
## 固定列:ID PS USER PASS
## ID
初始值 = str(L[0]) # 轉成字符串
字典_查詢字段_座標_初值[(0,0)] = 初始值 # 保存初始值
輸入框對象 = Entry(字段框, width=10, bg='#00BFFF')
輸入框對象.insert(0, 初始值)
輸入框對象.grid(row=0,column=0,sticky='W')
## PS
初始值 = str(L[1]) # 轉成字符串
字典_查詢字段_座標_初值[(0,1)] = 初始值 # 保存初始值
輸入框對象 = Entry(字段框, width=30, bg='#00BFFF')
輸入框對象.insert(0, 初始值)
輸入框對象.grid(row=0,column=1,sticky='W')
## USER
初始值 = str(L[2]) # 轉成字符串
字典_查詢字段_座標_初值[(0,2)] = 初始值 # 保存初始值
輸入框對象 = Entry(字段框, width=20, bg='#00BFFF')
輸入框對象.insert(0, 初始值)
輸入框對象.grid(row=0,column=2,sticky='W')
## PASS
初始值 = str(L[3]) # 轉成字符串
字典_查詢字段_座標_初值[(0,3)] = 初始值 # 保存初始值
輸入框對象 = Entry(字段框, width=50, bg='#00BFFF')
輸入框對象.insert(0, 初始值)
輸入框對象.grid(row=0,column=3,sticky='W')
## 數據值
for 行 in range(0, 行數):
## 固定列:ID PS USER PASS
## ID
初始值 = str(LL[行][0]) # 轉成字符串
字典_查詢結果_座標_初值[(行,0)] = 初始值 # 保存初始值
輸入框對象 = Entry(數據框, width=10)
輸入框對象.insert(0, 初始值)
輸入框對象.grid(row=行,column=0,sticky='W')
輸入框對象.bind("<Button-3>", DEF_彈出_數據框_右鍵菜單) # 每個控件對象都綁定右鍵菜單事件
## PS
初始值 = str(LL[行][1]) # 轉成字符串
字典_查詢結果_座標_初值[(行,1)] = 初始值 # 保存初始值
輸入框對象 = Entry(數據框, width=30)
輸入框對象.insert(0, 初始值)
輸入框對象.grid(row=行,column=1,sticky='W')
輸入框對象.bind("<Button-3>", DEF_彈出_數據框_右鍵菜單) # 每個控件對象都綁定右鍵菜單事件
## USER
初始值 = str(LL[行][2]) # 轉成字符串
字典_查詢結果_座標_初值[(行,2)] = 初始值 # 保存初始值
輸入框對象 = Entry(數據框, width=20)
輸入框對象.insert(0, 初始值)
輸入框對象.grid(row=行,column=2,sticky='W')
輸入框對象.bind("<Button-3>", DEF_彈出_數據框_右鍵菜單) # 每個控件對象都綁定右鍵菜單事件
## PASS
初始值 = str(LL[行][3]) # 轉成字符串
字典_查詢結果_座標_初值[(行,3)] = 初始值 # 保存初始值
輸入框對象 = Entry(數據框, width=50)
輸入框對象.insert(0, 初始值)
輸入框對象.grid(row=行,column=3,sticky='W')
輸入框對象.bind("<Button-3>", DEF_彈出_數據框_右鍵菜單) # 每個控件對象都綁定右鍵菜單事件
LabelFrame_顯編框 = LabelFrame(top, text='顯示/編輯框', bg='#FFD700')
LabelFrame_顯編框.grid(row=4,column=0,sticky='NW')
##
########
######## SQL命令框
##
## 執行用戶輸入的SQL語句
def DEF_按鈕_執行SQL語句():
SQL_CMD = 文本框_命令行.get(1.0, END).rstrip('\n') # 獲取編寫的SQL語句,去掉後面的回車符號
if SQL_CMD.strip() != '':
R = DEF_SQL_執行(SQL_CMD) # 調用非查詢語句函數
if R[0] == 0:
INFO = f'數據庫執行SQL語句 {SQL_CMD} 成功'
打開數據庫() ## 更新一下數據庫顯示
tkinter.messagebox.showinfo(title='成功', message=INFO)
else:
ERROR = f'數據庫執行SQL語句 {SQL_CMD} 失敗 {R[1]}'
tkinter.messagebox.showerror(title='失敗', message=ERROR)
else:
ERROR = '沒有輸入SQL語句'
tkinter.messagebox.showerror(title='錯誤', message=ERROR)
## 執行用戶輸入的SQL腳本
def DEF_按鈕_執行SQL腳本():
SQL_CMD = 文本框_命令行.get(1.0, END).rstrip('\n') # 獲取編寫的SQL語句,去掉後面的回車符號
if SQL_CMD.strip() != '':
R = DEF_SQL_執行腳本(SQL_CMD) # 調用非查詢語句函數
if R[0] == 0:
INFO = f'數據庫執行SQL腳本 {SQL_CMD} 成功'
打開數據庫() ## 更新一下數據庫顯示
tkinter.messagebox.showinfo(title='成功', message=INFO)
else:
ERROR = f'數據庫執行SQL腳本 {SQL_CMD} 失敗 {R[1]}'
tkinter.messagebox.showerror(title='失敗', message=ERROR)
else:
ERROR = 'SQL腳本無內容'
tkinter.messagebox.showerror(title='錯誤', message=ERROR)
## 清除命令框裏的內容
def DEF_按鈕_清屏():
文本框_命令行.delete(0.0, END)
文本框_命令行.focus_set()
命令框 = LabelFrame(top, text='SQL語句/SQL腳本')
命令框.grid(row=5,column=0,sticky='NW')
命令行_按鈕框 = Text(命令框)
命令行_按鈕框.grid(row=1,column=0)
Button(命令行_按鈕框, text='執行SQL語句', command=DEF_按鈕_執行SQL語句).grid(row=0, column=0)
Button(命令行_按鈕框, text='執行SQL腳本', command=DEF_按鈕_執行SQL腳本).grid(row=0, column=1)
Button(命令行_按鈕框, text='清屏', command=DEF_按鈕_清屏).grid(row=0, column=2)
文本框_命令行 = Text(命令框, height=3, wrap='none')
文本框_命令行.grid(row=2,column=0,sticky='NESW')
Scrollbar_命令框_豎 = Scrollbar(命令框)
Scrollbar_命令框_豎['command'] = 文本框_命令行.yview
Scrollbar_命令框_橫 = Scrollbar(命令框)
Scrollbar_命令框_橫['command'] = 文本框_命令行.xview
Scrollbar_命令框_橫['orient'] = HORIZONTAL
Scrollbar_命令框_豎.grid(row=2, column=1, sticky=S+W+E+N)
Scrollbar_命令框_橫.grid(row=3, column=0, sticky=S+W+E+N)
文本框_命令行.config(xscrollcommand=Scrollbar_命令框_橫.set, yscrollcommand=Scrollbar_命令框_豎.set) # 自動設置滾動條滑動幅度
## 開軟件後先初始化數據庫
## 如果PWD表不存在則創建表
SQL_CMD = '''CREATE TABLE IF NOT EXISTS PWD(
ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
PS TEXT,
USER TEXT,
PASS TEXT);'''
R = DEF_SQL_執行(SQL_CMD)
if R[0] == 0:
print("創建表PWD成功")
else:
ERROR = f'創建表失敗:{R[1]}'
tkinter.messagebox.showerror(title='ERROR', message=ERROR)
## 再打開數據庫
打開數據庫()
# 進入消息循環
top.mainloop()