本篇學習筆記将利用輕量級資料庫TinyDB存儲資訊,配合Qt多視窗使用者界面,實作資料庫新增、修改、删除和查詢等功能,最終使用Qt的表格元件将資訊展現出來。
一、輕量級資料庫TinyDB介紹
Python中的TinyDB,是一個純Python編寫的輕量級的面向文檔的資料庫,源碼一共隻有1800行代碼,沒有外部依賴項。TinyDB的資料儲存方式主要是json檔案,也就是 Python 中的字典類型。對于小型項目而言,使用TinyDB資料庫非常适合。它有如下特點:
- 輕便:源代碼很少,小巧輕便
- 可随意遷移:TinyDB基于面向文檔的存儲,資料庫隻有一個json檔案,不依賴任何服務,拷貝即可遷移
- 易用性:TinyDB的API非常簡單和幹淨,易于使用
- 非依賴性:TinyDB既不需要外部伺服器,也不需要任何來自PyPI的依賴項,支援Python3.6以上的所有版本
- 可擴充性:在中間件的幫助下,可以輕松擴充TinyDB行為,以滿足特定的需求
(一)建立json資料庫和表
TinyDB是一個第三方包,在使用之前需要先安裝,如指令行格式:pip install tinydb,或者在內建開發環境中安裝。
建立資料庫和表的代碼模闆如下:
from tinydb import TinyDB # 導入TinyDB類
db=TinyDB('my_db.json') # 建立名為my_db的json資料庫
table=db.table('student') # 建立名為student的表
db.close() # 關閉資料庫
(二)插入資料
1、插入單條資料
table.insert({'name':'張三','age':23,'class':'三年五班'})
table.insert({'name':'李四','age':'30','class':'三年一班'})
【注】插入語句insert的傳回值是所添加資料在表中的索引值,表的索引值從1開始,順序遞增。運作以下代碼觀察索引值變化情況:
from tinydb import TinyDB
db=TinyDB('my_db.json')
table=db.table('student')
index1=table.insert({'name':'張三','age':23,'class':'三年五班'})
index2=table.insert({'name':'李四','age':'30','class':'三年一班'})
print(index1,index2)
db.close()
運作結果:
第一次運作的結果是1 2,第二次是3 4,依此類推……
2、插入多條資料
from tinydb import TinyDB
db = TinyDB('my_db.json')
table = db.table('student')
data = [{'name': '小明', 'age': 13, 'class': '三年五班'}, {'name': '小剛', 'age': '12', 'class': '三年一班'},
{'name': '小強', 'age': '15', 'class': '三年一班'}, {'name': '小紅', 'age': '13', 'class': '三年一班'},
{'name': '小花', 'age': '14', 'class': '三年一班'}]
index = table.insert_multiple(data) # 插入多條資料,即資料以清單形式添加
print(index)
db.close()
運作結果:[1, 2, 3, 4, 5]
【注】在插入多條資料時,索引值是一個清單。
(三)查詢資料
1、查詢資料庫中的表
num=db.tables()
print(num)
運作結果:
{'student', '_default'}
【注】'_default'表是預設存在的
2、使用all函數查詢所有資料
string=table.all()
print(string) # 輸出all函數查詢到的所有結果
運作結果:
[{'name': '小明', 'age': 13, 'class': '三年五班'}, {'name': '小剛', 'age': '12', 'class': '三年一班'}, {'name': '小強', 'age': '15', 'class': '三年一班'}, {'name': '小紅', 'age': '13', 'class': '三年一班'}, {'name': '小花', 'age': '14', 'class': '三年一班'}, {'name': '小明', 'age': 13, 'class': '三年五班'}, {'name': '小剛', 'age': '12', 'class': '三年一班'}, {'name': '小強', 'age': '15', 'class': '三年一班'}, {'name': '小紅', 'age': '13', 'class': '三年一班'}, {'name': '小花', 'age': '14', 'class': '三年一班'}]
3、使用where進行條件查詢
from tinydb import TinyDB, where # 需要導入where
str1 = table.search(where('name') == '小花') # 在表中查詢名字是小花的記錄
str2 = table.search(where('age') > 13)
print(str1)
print(str2)
運作結果:
[{'name': '小花', 'age': '14', 'class': '三年一班'}]
[{'name': '小強', 'age': 15, 'class': '三年一班'}, {'name': '小花', 'age': 14, 'class': '三年一班'}]
4、使用Query查詢
Query是資料庫查詢元件,使用非常友善靈活,需要事先導入Query。示範代碼如下:
stuQuery = Query() # 建立查詢對象
query_data = table.search(stuQuery.cls == '三年一班') # 查詢班級是三年一班的記錄
print(query_data)
query_data = table.search(stuQuery.age <= 13) # 查詢年齡小于等于13的記錄
print(query_data)
query_data=table.search(stuQuery.age<=13 and stuQuery.cls=='三年五班')
print(query_data)
query_data=table.search((stuQuery.age<13) | (stuQuery.age>14))
print(query_data)
運作結果:
[{'name': '小剛', 'age': 12, 'cls': '三年一班'}, {'name': '小強', 'age': 15, 'cls': '三年一班'}, {'name': '小紅', 'age': 13, 'cls': '三年一班'}, {'name': '小花', 'age': 14, 'cls': '三年一班'}]
[{'name': '小明', 'age': 13, 'cls': '三年五班'}, {'name': '小剛', 'age': 12, 'cls': '三年一班'}, {'name': '小紅', 'age': 13, 'cls': '三年一班'}]
[{'name': '小明', 'age': 13, 'cls': '三年五班'}]
[{'name': '小剛', 'age': 12, 'cls': '三年一班'}, {'name': '小強', 'age': 15, 'cls': '三年一班'}]
【注】資料庫中的字段名一定不能與系統的保留關鍵字重複,例如上面的學生資料庫的班級字段,如果寫成class,那麼在使用Query查詢時程式就會出錯。
(四)修改資料
1、使用where方法
index = table.update({'cls': '三年二班'}, where('name') == '小剛')
print('修改記錄的索引是:', index)
print(table.search(where('name') == '小剛'))
運作結果:
修改記錄的索引是: [2]
[{'name': '小剛', 'age': 12, 'cls': '三年二班'}]
2、使用Query方法
index=table.update({'cls': '三年三班'}, stuQuery.name=='小強')
print('修改記錄的索引是:', index)
print(table.search(where('name') == '小強'))
運作結果:
修改記錄的索引是: [3]
[{'name': '小強', 'age': 15, 'cls': '三年三班'}]
(五)删除資料
1、删除所有資料
table.truncate()
print(table.all())
運作結果:
[]
2、删除符合條件的資料
del_index = table.remove(stuQuery.cls == '三年一班') # 删除班級是三年一班的資料
print('删除的索引是:', del_index)
print(table.all())
運作結果:
删除的索引是: [4, 5]
[{'name': '小明', 'age': 13, 'cls': '三年五班'}, {'name': '小剛', 'age': 12, 'cls': '三年二班'}, {'name': '小強', 'age': 15, 'cls': '三年三班'}]
二、多視窗圖形界面介紹
在實際開發項目時,多視窗圖形界面是經常要使用的,例如登入視窗、點選功能按鈕彈出對應功能實作視窗等等。使用多視窗圖形界面,必須要理清視窗的繼承關系,以及設定視窗的模态屬性,也就是子視窗是否阻塞主視窗。
(一)建立多視窗的基本模型
下面執行個體示範建立多視窗的基礎模型。首先,在Qt設計大師中建立一個主視窗和兩個子視窗,主視窗設定了背景顔色,分别儲存這三個視窗檔案,并将三個視窗的ui檔案轉換成py檔案,準備靜态加載。
1、加載多視窗代碼
from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtGui import Qt
from ui_test1 import Ui_MForm # 導入視窗1的Ui類
from ui_test2 import Ui_SForm1 # 導入視窗2的Ui類
from ui_test3 import Ui_SForm2 # 導入視窗3的Ui類
# 自定義主視窗的類
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.ui1 = Ui_MForm()
self.ui1.setupUi(self)
# 設定兩個子視窗對象值為None,代表視窗未打開
self.s_window1 = None
self.s_window2 = None
# 主視窗的按鈕單擊信号和槽
self.ui1.pushButton.clicked.connect(self.show_Form)
# 顯示子視窗的方法
def show_Form(self):
# 如果子視窗1是未打開狀态,則建立該視窗
# 沒有這個檢測的環節,點選打開視窗按鈕會反複打開子視窗
if self.s_window1 is None:
self.s_window1 = SubWindow1()
self.s_window1.show()
self.s_window1.move(300, 200)
# 如果子視窗2是未打開狀态,則建立該視窗
if self.s_window2 is None:
self.s_window2 = SubWindow2()
self.s_window2.show()
self.s_window2.move(900, 200)
# 自定義子視窗1的類,繼承主視窗的類MainWindow,是以主視窗的背景顔色設定在子視窗1裡生效
class SubWindow1(MainWindow):
def __init__(self):
super(SubWindow1, self).__init__()
self.ui2 = Ui_SForm1()
self.ui2.setupUi(self)
# 自定義子視窗2的類,繼承通用元件QWidget,是以主視窗的背景顔色設定在子視窗2裡不生效
class SubWindow2(QWidget):
def __init__(self):
super(SubWindow2, self).__init__()
self.ui3 = Ui_SForm2()
self.ui3.setupUi(self)
if __name__ == '__main__':
app = QApplication([])
window = MainWindow()
window.show()
app.exec()
【注】子視窗1的自定義類繼承了主視窗的自定義類MainWindow,子視窗2的自定義類繼承通用元件QWidget,是以子視窗1的背景顔色跟主視窗一樣,而子視窗2沒有背景顔色。
2、效果示範
視訊加載中...
(二)實作子視窗跟随主視窗一起關閉
在上面多視窗基本模型的視訊示範中可以看到,主視窗和兩個子視窗可以自由關閉,但是當主視窗關閉時,子視窗并不能跟随關閉。為了解決這個問題,我們要重寫主類的closeEvent關閉事件方法。
1、重寫主類closeEvent方法代碼
# 一定要在主視窗的自定義類裡書寫此代碼
def closeEvent(self, event):
sys.exit(0)
2、效果示範
視訊加載中...
【注】程式運作後,關閉主視窗則子視窗1和子視窗2都跟着關閉;關閉子視窗1,主視窗和子視窗2也跟着關閉,因為子視窗1繼承自主視窗類,關閉事件跟主視窗是相同的;關閉子視窗2不會影響主視窗和子視窗1。
(三)設定子視窗模态屬性
在做多視窗項目開發時,有時要求子視窗不能同時打開,以免處理不好發生資料混亂的情況,例如新增資料和删除資料子視窗。為此,我們就需要用子視窗來阻塞主視窗程序,這裡就需要設定子視窗的模态屬性。
1、在Qt設計大師中設定
點選需要設定模态屬性的子視窗,在視窗的QWidget屬性設定中找到windowModality,将屬性值改成ApplicationModal,儲存視窗并将相應的ui檔案轉換成py檔案即可。模态屬性設定如下圖:
設定子視窗的模态屬性
2、在代碼中設定
self.s_window2 = SubWindow2()
# 子視窗模态屬性設定一定要放在show()方法之前
self.s_window2.setWindowModality(Qt.ApplicationModal)
self.s_window2.show()
(四)多視窗示範完整代碼
import sys
from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtGui import Qt
from ui_test1 import Ui_MForm # 導入視窗1的Ui類
from ui_test2 import Ui_SForm1 # 導入視窗2的Ui類
from ui_test3 import Ui_SForm2 # 導入視窗3的Ui類
# 自定義主視窗的類
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.ui1 = Ui_MForm()
self.ui1.setupUi(self)
# 設定兩個子視窗對象值為None,代表視窗未打開
self.s_window1 = None
self.s_window2 = None
# 主視窗的按鈕單擊信号和槽
self.ui1.pushButton.clicked.connect(self.show_Form)
# 顯示子視窗的方法
def show_Form(self):
# 如果子視窗1是未打開狀态,則建立該視窗
if self.s_window1 is None:
self.s_window1 = SubWindow1()
self.s_window1.show()
self.s_window1.move(300, 200)
# 如果子視窗2是未打開狀态,則建立該視窗
if self.s_window2 is None:
self.s_window2 = SubWindow2()
# 設定子視窗2的模态方式為阻塞主程式,或者在Qt設計大師的視窗屬性裡設定
# 程式運作後,隻有子視窗2是活動的,主視窗不可點選,隻有子視窗2關閉後才可以操作主視窗
self.s_window2.setWindowModality(Qt.ApplicationModal)
self.s_window2.show()
self.s_window2.move(900, 200)
# 重寫父類的關閉事件方法,實作關閉主視窗後子視窗跟着關閉的功能
def closeEvent(self, event):
sys.exit(0)
# 自定義子視窗1的類,繼承主視窗的類MainWindow,是以主視窗的背景顔色設定在子視窗1裡生效
class SubWindow1(MainWindow):
def __init__(self):
super(SubWindow1, self).__init__()
self.ui2 = Ui_SForm1()
self.ui2.setupUi(self)
# 自定義子視窗2的類,繼承通用元件QWidget,是以主視窗的背景顔色設定在子視窗2裡不生效
class SubWindow2(QWidget):
def __init__(self):
super(SubWindow2, self).__init__()
self.ui3 = Ui_SForm2()
self.ui3.setupUi(self)
if __name__ == '__main__':
app = QApplication([])
window = MainWindow()
window.show()
app.exec()
視訊加載中...
三、綜合執行個體開發
在介紹圖形界面開發之PySimpleGUI子產品時,我們使用SQLite3和PySimpleGUI子產品制作了一個員工檔案資訊管理系統,現在我們使用PySide6和TinyDB對該系統進行重新開發,旨在加深對PySide6和TinyDB的了解,并未在安全性方面做任何處理。
(一)準備資料庫
1、資料庫描述
本案例采用輕量級資料庫TinyDB存儲員工檔案資訊。資料庫、表和字段設定如下:
【資料庫檔案】:company.json
【檔案資訊表】:employee
【字段】:編号、姓名、性别、出生年月、民族、籍貫、黨派、學曆、職稱、身份證号碼
電話号碼、所在部門、獲得的榮譽、所受的處罰
【使用者名密碼儲存表】:user
字段:uid、pwd
2、初始化資料庫
首先建立一個init.py檔案,使用代碼建立資料庫和使用者名密碼儲存表,并插入一條預設使用者名和密碼資料,代碼如下:
from tinydb import TinyDB
db = TinyDB('company.json') # 建立json資料庫,資料庫名稱為company.json
table = db.table('user') # 建立用來存儲使用者名和密碼的表,表名為user
table.insert({'uid': 'admin', 'pwd': '1111'}) # 設定初始使用者名和密碼
db.close()
(二)設計視窗
視窗包括:主視窗,登入視窗,新增資訊視窗,查詢/删除資訊視窗,修改資訊視窗和修改密碼視窗。
1、主視窗
在Qt設計大師裡建立主視窗時,最好使用Main Window模版,如果不需要菜單欄、工具欄和位址欄可以手動去除。Main Window類的視窗功能比較健全,設定背景圖檔、布局等都不會出現問題。采用Form類建立的視窗,在代碼中建立視窗類時,如果繼承QMainWindow類,那麼布局會混亂;如果繼承QWidget類,那麼背景圖檔就會不顯示。
我在做這個執行個體時,就是采用Form模版建立的主視窗,然後在代碼中建立視窗類時繼承QWidget類,就發生了背景圖檔不顯示的情況,折磨了很久才找到原因。由于不想重新設計主視窗,于是将主視窗的最大尺寸和最小尺寸均設定成視窗實際尺寸,讓視窗不能通過拖動改變尺寸,也就不需要布局了,最後在建立視窗類的代碼中繼承QMainWindow類,總算解決了背景圖檔不顯示的問題。
- 視窗ui檔案名稱:ui_main.ui
- 視窗對象名稱:MainForm,由于是多視窗應用,是以視窗對象的名稱最好不采用預設的Form或者MainWindow
- 包含元件:四個Tool Button,工具按鈕的toolButtonStyle屬性設定為ToolButtonTextUnderIcon,即按鈕文本在圖示下面。autoRaise屬性設定為真,即工具按鈕自動浮起,否則是凹陷狀态,按鈕周圍有外框影響美觀
- 背景圖檔:在視窗對象的樣式表中,使用#MainForm{}的形式設定背景圖檔,這是為了隻給名稱為MainForm的元件即視窗添加背景圖檔,否則視窗上的所有元素都會加載這個背景圖檔。樣式表代碼如下:
#MainForm{background-image: url(:/images/icon/background.jpg);}
- 視窗外觀:如下圖
主視窗示例圖檔
2、登入視窗
設計登入視窗不要采用Dialog模版,無論點選OK還是Cancel按鈕,Dialog類的視窗都會關閉,不能持久顯示。
- 視窗ui檔案名稱:ui_login.ui
- 視窗對象名稱:LoginForm
- 包含元件:兩個标簽元件,兩個單行文本輸入框元件,一個按鈕元件
- 背景圖檔:與主視窗設定背景圖檔方法相同
- 視窗外觀:如下圖
登入視窗示例圖檔
3、新增資訊視窗
- 視窗ui檔案名稱:ui_add.ui
- 視窗對象名稱:AddForm
- 包含元件:一個Group Box容器類元件,用來裝載錄入員工基本資訊的所有元件,起到歸類的作用。一個Tab Widget标簽元件,用來放置“獲得的榮譽”和“所受的處罰”兩個多行文本輸入框,節省空間。一個Table Widget表格元件,用來顯示新錄入的員工資訊。剩餘是标簽、單行文本輸入框和按鈕元件,整個視窗設定了布局,支援拖放改變視窗大小。另外,在視窗的樣式表中對表格元件的标題欄進行了設定,樣式表代碼如下:
QHeaderView::section{;background-color:rgb(225,225,225);height:28px;}
- 背景圖檔:無
- 視窗外觀:如下圖
新增資訊視窗示例圖檔
4、查詢/删除資訊視窗
- 視窗ui檔案名稱:ui_search_del.ui
- 視窗對象名稱:SDForm
- 包含元件:一個Table Widget表格元件,标題欄設定與新增資訊中相同。剩餘是标簽、單行文本輸入框和按鈕元件,整個視窗設定了布局,支援拖放改變視窗大小
- 背景圖檔:無
- 視窗外觀:如下圖
查詢/删除資訊視窗示例圖檔
5、修改資訊視窗
- 視窗ui檔案名稱:ui_change.ui
- 視窗對象名稱:ChangeForm
- 包含元件:與新增資訊視窗基本相同
- 背景圖檔:無
- 視窗外觀:如下圖
修改資訊視窗示例圖檔
(三)代碼部分
1、登入視窗和主視窗顯示代碼
(1)思路分析
- 建立登入視窗類
- 建立主視窗類
- 建立登入視窗執行個體對象
- 顯示登入視窗,即程式運作首先顯示登入視窗
- 判斷登入是否成功,如果登入成功,則關閉登入視窗本身,然後建立主視窗執行個體對象并顯示主視窗
(2)執行個體代碼
from PySide6.QtWidgets import QApplication, QMainWindow, QMessageBox
from tinydb import TinyDB, Query
from ui_login import Ui_LoginForm # 導入登入視窗ui檔案
from ui_main import Ui_MainForm # 導入主視窗ui檔案
# 建立登入視窗類,這裡必須繼承QMainWindow類,如果繼承QWidget,那麼背景圖檔無法顯示
class LoginWindow(QMainWindow):
def __init__(self):
super(LoginWindow, self).__init__()
self.uiLogin = Ui_LoginForm()
self.uiLogin.setupUi(self)
self.main_window = None # 預設主視窗對象
# 密碼輸入框回車和登入按鈕單擊信号,都連接配接到登入方法
self.uiLogin.lineEdit_2.returnPressed.connect(self.login)
self.uiLogin.pushButton.clicked.connect(self.login)
# 登入方法
def login(self):
if self.uiLogin.lineEdit.text() == '' or self.uiLogin.lineEdit_2.text() == '':
return
db = TinyDB('company.json')
table = db.table('user')
query = Query()
username = 'admin'
# 搜尋結果是清單包含字典的格式,是以需要先用索引值獲得字典資料,再用字典的key擷取value值
password = table.search(query.uid == username)[0]['pwd']
if self.uiLogin.lineEdit.text() == username and self.uiLogin.lineEdit_2.text() == password:
self.close() # 如果登入成功,則登入視窗本身關閉。因為不再使用登入視窗,是以沒有使用隐藏方法。
self.main_window = MainWindow() # 建立主視窗執行個體對象
self.main_window.show() # 顯示主視窗
else:
QMessageBox.information(self, '提示資訊', '使用者名或密碼錯誤,請重新輸入!')
# 建立主視窗類,同樣要繼承QMainWindow類,以顯示背景圖檔
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.uiMain = Ui_MainForm()
self.uiMain.setupUi(self)
if __name__ == '__main__':
app = QApplication([])
window = LoginWindow() # 建立登入視窗執行個體對象
window.show() # 顯示登入視窗
app.exec()
2、主程式完整代碼
在主程式中,除了上面介紹的登入視窗和主程式視窗顯示代碼外,剩下就是顯示新增資訊、查詢/删除資訊和修改資訊視窗的代碼。每個視窗具體功能實作部分,都在各自的py檔案中,在主程式中将這些py檔案以子產品的形式導入,再引用子產品内的視窗類就可以了。修改密碼功能,在主程式中直接采用QInputDialog輸入對話框完成。主程式檔案名為mainProgram.py,完整代碼如下:
from PySide6.QtWidgets import QApplication, QMainWindow, QMessageBox, QInputDialog
from PySide6.QtGui import Qt
from tinydb import TinyDB, Query
from ui_login import Ui_LoginForm
from ui_main import Ui_MainForm
from add import AddWindow # 導入新增資訊子產品即py檔案中的視窗類
from search_del import SDWindow # 導入搜尋/删除資訊子產品中的視窗類
from change import ChangeWindow # 導入修改資訊子產品中的視窗類
# 建立登入視窗類,這裡必須繼承QMainWindow類,如果繼承QWidget,那麼背景圖檔無法顯示
class LoginWindow(QMainWindow):
def __init__(self):
super(LoginWindow, self).__init__()
self.uiLogin = Ui_LoginForm()
self.uiLogin.setupUi(self)
self.main_window = None # 預設主視窗對象
# 密碼輸入框回車和登入按鈕單擊信号,都連接配接到登入方法
self.uiLogin.lineEdit_2.returnPressed.connect(self.login)
self.uiLogin.pushButton.clicked.connect(self.login)
# 登入方法
def login(self):
if self.uiLogin.lineEdit.text() == '' or self.uiLogin.lineEdit_2.text() == '':
return
db = TinyDB('company.json')
table = db.table('user')
query = Query()
username = 'admin'
# 搜尋結果是清單包含字典的格式,是以需要先用索引值獲得字典資料,再用字典的key擷取value值
password = table.search(query.uid == username)[0]['pwd']
db.close()
if self.uiLogin.lineEdit.text() == username and self.uiLogin.lineEdit_2.text() == password:
self.close() # 如果登入成功,則登入視窗本身關閉。因為不再使用登入視窗,是以沒有使用隐藏方法。
self.main_window = MainWindow() # 建立主視窗執行個體對象
self.main_window.show() # 顯示主視窗
else:
QMessageBox.information(self, '提示資訊', '使用者名或密碼錯誤,請重新輸入!')
# 建立主視窗類,同樣要繼承QMainWindow類,以顯示背景圖檔
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.uiMain = Ui_MainForm()
self.uiMain.setupUi(self)
self.add_window = None # 預設新增資訊視窗對象
self.search_window = None # 預設搜尋删除資訊視窗對象
self.change_window = None # 預設修改資訊視窗對象
self.uiMain.toolButton_add.clicked.connect(self.show_add) # 新增資訊按鈕點選信号和槽
self.uiMain.toolButton_search.clicked.connect(self.show_search) # 查詢删除資訊按鈕點選信号和槽
self.uiMain.toolButton_change.clicked.connect(self.show_change) # 新增資訊按鈕點選信号和槽
self.uiMain.toolButton_pwd.clicked.connect(self.show_pwd) # 修改密碼按鈕單擊信号和槽
# 顯示新增資訊視窗方法
def show_add(self):
self.add_window = AddWindow()
self.add_window.setWindowModality(Qt.ApplicationModal) # 為視窗設定模态屬性
self.add_window.show()
# 顯示查詢/删除資訊視窗方法
def show_search(self):
self.search_window = SDWindow()
self.search_window.setWindowModality(Qt.ApplicationModal) # 為視窗設定模态屬性
self.search_window.show()
# 顯示修改資訊視窗方法
def show_change(self):
self.change_window = ChangeWindow()
self.change_window.setWindowModality(Qt.ApplicationModal) # 為視窗設定模态屬性
self.change_window.show()
# 修改密碼方法
def show_pwd(self):
pwd1, okPressed1 = QInputDialog.getText(self, '修改密碼', '請輸入新密碼:')
if okPressed1:
pwd2, okPressed2 = QInputDialog.getText(self, '确認修改', '請再次輸入新密碼:')
if okPressed2:
if pwd1 == pwd2:
db = TinyDB('company.json')
table = db.table('user')
query = Query()
table.update({'pwd': pwd1}, query.uid == 'admin')
db.close()
QMessageBox.information(self, '修改密碼', '密碼修改成功!')
else:
QMessageBox.information(self, '修改密碼', '兩次輸入不相同,請重新輸入!')
if __name__ == '__main__':
app = QApplication([])
window = LoginWindow() # 建立登入視窗執行個體對象
window.show() # 顯示登入視窗
app.exec()
3、新增資訊子產品代碼
新增資訊子產品名為add.py,代碼如下:
import copy
from PySide6.QtWidgets import QApplication, QWidget, QMessageBox, QTableWidgetItem
from PySide6.QtGui import Qt
from tinydb import TinyDB
from ui_add import Ui_AddForm
# 建立新增資訊視窗類
class AddWindow(QWidget):
def __init__(self):
super(AddWindow, self).__init__()
self.uiAdd = Ui_AddForm()
self.uiAdd.setupUi(self)
self.add_list = [] # 定義存儲所有新增資訊的清單,為的是往資料庫裡添加
self.add_dict = dict() # 定義存儲每行新增資訊的字典
self.uiAdd.pushButton.clicked.connect(self.addList) # 添加到新增清單按鈕信号和槽
self.uiAdd.pushButton_2.clicked.connect(self.saveDB) # 儲存到資料庫按鈕信号和槽
self.table_init() # 調用table表格初始化方法
self.id_init() # 調用編号初始化方法
# 編号初始化方法,自動生成編号
def id_init(self):
db = TinyDB('company.json')
table = db.table('employee')
content = table.all() # 讀取資料表的所有内容
rows = self.uiAdd.tableWidget.rowCount() # 擷取Table的行數
if rows == 0 and content == []:
self.uiAdd.edit_0.setText('A001') # 如果資料表和Table中的内容都是空的,則編号設定為A001
self.uiAdd.edit_1.setFocus() # 将焦點設定在姓名輸入框
return
if rows != 0: # 如果Table中有資料,則取得最後一行的編号
id_data = self.uiAdd.tableWidget.item(rows - 1, 0).text()
else:
id_data = content[-1]['編号'] # 如果Table中沒有資料,則取得資料表中最後一條記錄的編号資料
id_new = 'A%03d' % (int(id_data[1:]) + 1) # 将原有編号後面數字加1,一共顯示3位,前面用0補充
self.uiAdd.edit_0.setText(id_new) # 将新增資訊視窗的編号輸入框内容設定為新編号
self.uiAdd.edit_1.setFocus() # 将焦點設定在姓名輸入框
# 表格初始化方法
def table_init(self):
# 調整各列的列寬
self.uiAdd.tableWidget.setColumnWidth(0, 50)
self.uiAdd.tableWidget.setColumnWidth(1, 60)
self.uiAdd.tableWidget.setColumnWidth(2, 40)
self.uiAdd.tableWidget.setColumnWidth(3, 60)
self.uiAdd.tableWidget.setColumnWidth(4, 40)
self.uiAdd.tableWidget.setColumnWidth(5, 60)
self.uiAdd.tableWidget.setColumnWidth(6, 60)
self.uiAdd.tableWidget.setColumnWidth(7, 60)
self.uiAdd.tableWidget.setColumnWidth(8, 80)
self.uiAdd.tableWidget.setColumnWidth(9, 150)
self.uiAdd.tableWidget.setColumnWidth(10, 90)
# 添加到新增清單按鈕單擊方法
def addList(self):
# 判斷編号和姓名輸入框内容是否為空
if self.uiAdd.edit_0.text() == '' or self.uiAdd.edit_1.text() == '':
QMessageBox.critical(self, '錯誤資訊', '編号和姓名不能為空,請重新輸入')
else:
row = self.uiAdd.tableWidget.rowCount() # 擷取表格總行數
self.uiAdd.tableWidget.setRowCount(row + 1) # 表格目前行等于總行數加1
# 将單行文本輸入框的名稱設定為連續序列形式,如edit_0、edit_1……,利用eval函數循環将各個單行文本輸入框内容添加到表格目前行中
for col in range(12):
label_text = eval(f'self.uiAdd.label_{col}.text()')
edit_text = eval(f'self.uiAdd.edit_{col}.text()')
self.uiAdd.tableWidget.setItem(row, col, QTableWidgetItem(edit_text))
# 設定單元格對齊方式,需要使用Qt類的AlignmentFlag屬性
self.uiAdd.tableWidget.item(row, col).setTextAlignment(Qt.AlignmentFlag.AlignCenter)
# 建構新增資訊的字典資料,下面語句還可以寫成self.add_dict.update({f'{label_text}':f'{edit_text}'})
self.add_dict[f'{label_text}'] = edit_text
# 單行文本輸入框内容添加完畢後,将兩個多行文本輸入框的内容添加到最後兩列中
edit_text = self.uiAdd.plainTextEdit_0.toPlainText()
self.uiAdd.tableWidget.setItem(row, 12, QTableWidgetItem(edit_text))
self.add_dict['獲得的榮譽'] = edit_text # 字典裡也添加該資訊
edit_text = self.uiAdd.plainTextEdit_1.toPlainText()
self.uiAdd.tableWidget.setItem(row, 13, QTableWidgetItem(edit_text))
self.add_dict['所受的處罰'] = edit_text
# 将一行資訊的字典添加到清單中,這裡必須對字典進行深拷貝,否則下次輸入改變字典内容,清單内容随着更改
self.add_list.append(copy.deepcopy(self.add_dict))
# 使用跟上面相同方法,将所有輸入框内容清除
for i in range(12):
eval(f'self.uiAdd.edit_{i}.clear()')
self.uiAdd.plainTextEdit_0.clear()
self.uiAdd.plainTextEdit_1.clear()
self.id_init() # 所有輸入框内容清空後,調用編号初始化方法,自動生成編号
# 儲存到資料庫按鈕單擊方法
def saveDB(self):
choice = QMessageBox.question(self, '确認資訊', '确認要将資料儲存到資料庫嗎?')
if choice == QMessageBox.No:
return
db = TinyDB('company.json')
table = db.table('employee')
table.insert_multiple(self.add_list)
QMessageBox.information(self, '提示資訊', '資料儲存成功!')
print(table.all())
self.uiAdd.tableWidget.clearContents() # 清除表格内容,但不清除标題,clear方法會連标題都清除
self.uiAdd.tableWidget.setRowCount(0)
self.add_list.clear()
if __name__ == '__main__':
app = QApplication([])
window = AddWindow()
window.show()
app.exec()
運作效果:
視訊加載中...
4、查詢/删除資訊子產品代碼
新增資訊子產品名為search_del.py,代碼如下:
from PySide6.QtWidgets import QApplication, QWidget, QTableWidgetItem, QMessageBox
from PySide6 import QtCore
from PySide6.QtGui import Qt
from ui_search_del import Ui_SDForm
from tinydb import TinyDB, Query
class SDWindow(QWidget):
def __init__(self):
super(SDWindow, self).__init__()
self.uiSD = Ui_SDForm()
self.uiSD.setupUi(self)
self.table_init() # 調用表格初始化方法
self.uiSD.button_s.clicked.connect(self.search) # 送出搜尋按鈕單擊信号
self.uiSD.button_d.clicked.connect(self.delete) # 删除按鈕單擊信号
# 表格初始化方法
def table_init(self):
# 調整各列的列寬
self.uiSD.tableWidget.setColumnWidth(0, 50)
self.uiSD.tableWidget.setColumnWidth(1, 60)
self.uiSD.tableWidget.setColumnWidth(2, 40)
self.uiSD.tableWidget.setColumnWidth(3, 60)
self.uiSD.tableWidget.setColumnWidth(4, 40)
self.uiSD.tableWidget.setColumnWidth(5, 60)
self.uiSD.tableWidget.setColumnWidth(6, 60)
self.uiSD.tableWidget.setColumnWidth(7, 60)
self.uiSD.tableWidget.setColumnWidth(8, 80)
self.uiSD.tableWidget.setColumnWidth(9, 150)
self.uiSD.tableWidget.setColumnWidth(10, 90)
def search(self):
db = TinyDB('company.json')
table = db.table('employee')
query = Query()
self.query_data = []
# 按照所輸入位置和内容搜尋資訊
if self.uiSD.edit_id.text():
self.query_data = table.search(query.編号 == self.uiSD.edit_id.text())
if self.uiSD.edit_name.text():
self.query_data = table.search(query.姓名 == self.uiSD.edit_name.text())
if self.uiSD.edit_card.text():
self.query_data = table.search(query.身份證号碼 == self.uiSD.edit_card.text())
if self.uiSD.edit_cls.text():
self.query_data = table.search(query.所在部門 == self.uiSD.edit_cls.text())
db.close()
self.show_data(self.query_data) # 調用顯示資料方法
# 顯示資料方法
def show_data(self, data):
row = 0 # 初始行為0
self.uiSD.tableWidget.setRowCount(len(data)) # 設定表格總行數為搜尋到的記錄數
for item in data:
col = 0 # 初始列為0
for key in item:
self.uiSD.tableWidget.setItem(row, col, QTableWidgetItem(item[key]))
# 設定單元格對齊方式,需要使用Qt類的AlignmentFlag屬性
self.uiSD.tableWidget.item(row, col).setTextAlignment(Qt.AlignmentFlag.AlignCenter)
col += 1
row += 1
# 删除資訊按鈕單擊方法
def delete(self):
selected_data = self.uiSD.tableWidget.selectedItems() # 擷取選中的所有表格項目
self.id_list = [] # 定義存儲選中資訊的編号清單
rows = len(selected_data) // 14 # 計算一共選中幾行資訊,因為擷取的資料是所有單元格,每行有14個單元格
for i in range(rows):
self.id_list.append(selected_data[i * 14].text()) # 循環擷取所有準備删除的編号資料,放置在清單中
choice = QMessageBox.question(self, '删除資訊', f'确認要删除編号為:{self.id_list}的資料嗎?')
if choice == QMessageBox.No:
return
db = TinyDB('company.json')
table = db.table('employee')
query = Query()
# 将資料庫中包含要删除編号的記錄全部删掉
for i in self.id_list:
table.remove(query.編号 == i)
db.close()
self.search() # 調用查詢方法,更新表格顯示
QMessageBox.information(self,'删除資訊','資訊删除成功!')
if __name__ == '__main__':
app = QApplication([])
window = SDWindow()
window.show()
app.exec()
運作效果:
視訊加載中...
5、修改資訊子產品代碼
修改資訊子產品名為change.py,代碼如下:
from PySide6.QtWidgets import QApplication, QWidget, QMessageBox
from tinydb import TinyDB, Query
from ui_change import Ui_ChangeForm
# 建立修改資訊視窗類
class ChangeWindow(QWidget):
def __init__(self):
super(ChangeWindow, self).__init__()
self.uiChange = Ui_ChangeForm()
self.uiChange.setupUi(self)
self.load_data = [] # 預設存儲要修改資訊清單變量
self.uiChange.pushButton.clicked.connect(self.load) # 讀入資訊按鈕信号
self.uiChange.pushButton_2.clicked.connect(self.save) # 儲存資訊按鈕信号
# 讀入資訊方法
def load(self):
if not self.uiChange.edit_0.text():
QMessageBox.information(self, '提示資訊', '請輸入要修改資訊的編号,然後點讀入資訊按鈕!')
return
db = TinyDB('company.json')
table = db.table('employee')
query = Query()
self.load_data = table.search(query.編号 == self.uiChange.edit_0.text())
db.close()
if len(self.load_data) == 0:
QMessageBox.information(self, '提示資訊', '輸入的編号不存在,請重新輸入!')
return
i = 0
for item in self.load_data[0].values():
if i in (0, 12, 13): # 跳過第0、12和13條項目
i+=1
continue
eval(f'self.uiChange.edit_{i}.setEnabled(True)') # 循環設定單行文本輸入框的可用狀态為真
eval(f'self.uiChange.edit_{i}.setText(item)') # 設定文本輸入框的内容為讀入的相應資料
i += 1
# 設定兩個多行文本編輯器的狀态和内容
self.uiChange.plainTextEdit_0.setEnabled(True)
self.uiChange.plainTextEdit_1.setEnabled(True)
self.uiChange.plainTextEdit_0.setPlainText(self.load_data[0]['獲得的榮譽'])
self.uiChange.plainTextEdit_1.setPlainText(self.load_data[0]['所受的處罰'])
# 儲存資訊方法
def save(self):
db = TinyDB('company.json')
table = db.table('employee')
query = Query()
for i in range(12): # 以相應的标簽的文本為key,以對應的單行文本輸入框的内容為value,更新資料庫内容
key=eval(f'self.uiChange.label_{i}.text()')
value=eval(f'self.uiChange.edit_{i}.text()')
table.update({key:value},query.編号==self.uiChange.edit_0.text())
table.update({'獲得的榮譽':self.uiChange.plainTextEdit_0.toPlainText()},query.編号==self.uiChange.edit_0.text())
table.update({'所受的處罰':self.uiChange.plainTextEdit_1.toPlainText()},query.編号==self.uiChange.edit_0.text())
QMessageBox.information(self,'提示資訊','資訊儲存成功!')
if __name__ == '__main__':
app = QApplication([])
window = ChangeWindow()
window.show()
app.exec()
運作效果:
視訊加載中...
6、修改密碼功能
修改密碼功能沒有設計視窗,而是在主程式中直接采用QInputDialog輸入對話框完成。代碼如下:
# 修改密碼方法
def show_pwd(self):
pwd1, okPressed1 = QInputDialog.getText(self, '修改密碼', '請輸入新密碼:')
if okPressed1:
pwd2, okPressed2 = QInputDialog.getText(self, '确認修改', '請再次輸入新密碼:')
if okPressed2:
if pwd1 == pwd2:
db = TinyDB('company.json')
table = db.table('user')
query = Query()
table.update({'pwd': pwd1}, query.uid == 'admin')
db.close()
QMessageBox.information(self, '修改密碼', '密碼修改成功!')
else:
QMessageBox.information(self, '修改密碼', '兩次輸入不相同,請重新輸入!')
(四)整體效果示範
視訊加載中...