我們在這裏使用的是一個輕量級數據庫sqlite。Python2.5.x以上的版本默認自帶了sqlite3,大家可以試一下如果能成功import sqlite3就ok了。
首先我們需要一些預備知識。操作數據庫中的數據需要學習一種叫做SQL的語言,推薦w3school的教程:
http://www.w3school.com.cn/sql/
不需要系統學習,簡單瞭解一下就好。然後還需要學習一下sqlite在python中的基本操作方法,教程在這裏:
http://www.runoob.com/sqlite/sqlite-python.html
有了數據庫的知識,我們先來完成initDB()函數,完成每次程序啓動後都需要做的一些初始化操作。注意,現在我們的current_row就不能初始化爲0了,所以__init__(self)
函數中self.current_row = 0
這一句就沒什麼用了,可以刪去。還有,這裏我們使用了os.path.exists()函數,所以需要額外導入os包:import os。
initDB()
def initDB(self):
if os.path.exists('info.db'):
self.conn = sqlite3.connect('info.db')
self.conn.isolation_level = None
else:
self.conn = sqlite3.connect('info.db')
self.conn.isolation_level = None
self.conn.execute('''CREATE TABLE INFO
(ID int PRIMARY KEY NOT NULL,
WEBSITE char(255),
USERNAME char(255),
PASSWORD char(255),
URL char(255))''')
cur = self.conn.cursor()
cur.execute('SELECT * FROM INFO')
self.displayData = cur.fetchall()
cur.close()
self.current_row = len(self.displayData)
前面比較好理解,如果存在這個數據庫,就和它建立聯繫;如果不存在的話,就新建一個。
self.conn.isolation_level = None
這裏我們修改隔離等級是爲了圖方便,每次對數據庫操作之後不用commit就可以生效。
注意我們每個項目除了之前的四個數據之外,還加入了’ID’這一數據,ID爲數據所處的行數(從1開始計數),目的是通過給每條數據編號的方式使查找某一行的數據變得簡單,我們後面就會看到。
cur = self.conn.cursor()
cur.execute('SELECT * FROM INFO')
self.displayData = cur.fetchall()
cur.close()
self.current_row = len(self.displayData)
這裏我們建立了一個遊標(cursor),用來讀取已經在數據庫中的數據,並且獲得數據的條數。
接下來,我們需要修改之前已經寫好的三個分別具有new、edit、delete功能的函數,使用戶進行的操作可以同步修改數據庫中的數據。
新版newAction_def()
def newAction_def(self):
data = self.showDialog()
if data[0]:
self.current_row += 1
self.conn.execute("INSERT INTO INFO VALUES(%d, '%s', '%s', '%s', '%s')"
% (self.current_row, data[1], data[2], data[3], data[4]))
self.grid.insertRow(self.current_row - 1)
for i in range(4):
new_item = QtGui.QTableWidgetItem(data[i + 1])
self.grid.setItem(self.current_row - 1, i, new_item)
這裏我們用SQL中的insert語句將新的數據插入數據庫內。同樣,注意區分清楚行數和行號。
另外注意,%s兩邊的單引號必須帶,想一想爲什麼。
新版editAction_def()
def editAction_def(self):
selected_row = self.grid.selectedItems()
if selected_row:
edit_row = self.grid.row(selected_row[0])
old_data = []
for i in range(4):
old_data.append(self.grid.item(edit_row, i).text())
new_data = self.showDialog(*old_data)
if new_data[0]:
self.conn.execute('''UPDATE INFO SET
WEBSITE = '%s', USERNAME = '%s',
PASSWORD = '%s', URL = '%s'
WHERE ID = '%d' '''
% (new_data[1], new_data[2], new_data[3], new_data[4], edit_row + 1))
for i in range(4):
new_item = QtGui.QTableWidgetItem(new_data[i + 1])
self.grid.setItem(edit_row, i, new_item)
else:
self.showHint()
修改操作同樣也比較簡單,我們使用ID查找到要修改的數據,在數據庫、窗口中分別修改它。
新版delAction_def()
def delAction_def(self):
selected_row = self.grid.selectedItems()
if selected_row:
del_row = self.grid.row(selected_row[0])
self.grid.removeRow(del_row)
print del_row
self.conn.execute("DELETE FROM INFO WHERE ID = %d" % (del_row + 1))
for index in range(del_row + 2, self.current_row + 1):
self.conn.execute("UPDATE INFO SET ID = %d WHERE ID = %d" % ((index - 1), index))
self.current_row -= 1
else:
self.showHint()
而del函數就要注意了,刪除某項之後,其後項目ID並不會自動改變,我們需要手動把後面的項目序號-1……就是下面這兩句:
for index in range(del_row + 2, self.current_row + 1):
self.conn.execute("UPDATE INFO SET ID = %d WHERE ID = %d" % ((index - 1), index))
順求更好方法解決這個問題。
最後,我們在程序結束後關閉數據庫,於是我們將main函數改成這樣:
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
pwk = PWKeeper()
pwk.show()
app.exec_()
pwk.conn.close()
sys.exit(0)
大功告成
這樣我們就大功告成啦!試着運行一下自己的密碼管理器,嘗試一下各種功能是否正常。關閉程序後,我們可以看見程序所在目錄下出現了一個.db的數據庫文件。
至此,我們已經完成了實現密碼管理器功能的所有代碼,有沒有很大的成就感呢!
程序設計中,代碼並不是最重要的,我們更需要掌握一些思想方法和學習方法,所以請務必閱讀一下看似沒有什麼用的下一節。
【持續更新中】