我们在这里使用的是一个轻量级数据库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。
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两边的单引号必须带,想一想为什么。
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查找到要修改的数据,在数据库、窗口中分别修改它。
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的数据库文件。
至此,我们已经完成了实现密码管理器功能的所有代码,有没有很大的成就感呢!
程序设计中,代码并不是最重要的,我们更需要掌握一些思想方法和学习方法,所以请务必阅读一下看似没有什么用的下一节。
【持续更新中】