PyQt5系列教程(47):極簡圖書管理(QTableWidget的使用)2
來自專欄 PyQt5圖形界面編程
上期我們介紹了QTableWidget和QTableWidgetItem的基本信息,並且通過一個視頻展示了一個極簡圖書管理系統。本篇開始我們的正式學習。
設計思路
像這種功能相對較多的小工具,一般會先談下設計思路。整個系統(其實就是小工具)的思路流程如下(個人思考路徑,不一定最優):
1、界面設計
這個我主要是通過Qt設計師完成的。
2、功能劃分:
2.1、新增圖書
新增圖書分為兩種方式:自動讀取網路圖書檔案、手動填寫圖書檔案。手動填寫這個不必說。自動讀取網路圖書檔案,這個數據從哪裡來呢?豆瓣API!豆瓣API提供了較為完善的圖書信息,採用HTTPS訪問方式,只要有圖書的ISBN號,就能查詢,使用非常方便。這裡科普一下什麼是ISBN號。國際標準書號(International Standard Book Number),簡稱ISBN,是專門為識別圖書等文獻而設計的國際編號(來源:百度百科)。
為了更加方便的使用豆瓣API,我自定義了一個GetBookInfo類,將所有有關豆瓣API的操作都放到這個類中。
2.2 查詢圖書
這個比較簡單,根據查詢的關鍵欄位,返回具體的表格行數。
2.3 數據查詢、存儲
這次的程序是具有存儲功能的,我們存儲的載體本質是就是一個文件。通過Python中pickle模塊,實現圖書檔案的存儲、刪除、新增、修改等操作。這些操作我也是封裝到DataManagement類中,方便調用。
2.4 數據呈現
這裡主要就是對QTableWidget進行操作了,包括修改數據、刪除數據。
總結一下,如下圖所示:
豆瓣API使用
豆瓣API的文檔如下:https://developers.douban.com/wiki/?title=guide
有興趣的學友可以自己去看看,內容也比較豐富。
我們這裡主要是使用讀書API:https://developers.douban.com/wiki/?title=book_v2
這個API的調用地址如下:
根據isbn獲取圖書信息GET https://api.douban.com/v2/book/isbn/:name同上,返回圖書信息,返回status=200
這裡的name就是ISBN號。
它會返回json格式的數據,類似下面這種的。
{ "id":"1220562", "alt":"https://book.douban.com/book/1220562", "rating":{"max":10, "average":"7.0", "numRaters":282, "min":0}, "author":[{"name":"片山恭一"}, {"name":"豫人"}], "alt_title":"", "image":"https://img3.doubanio.com/spic/s1747553.jpg", "title":"滿月之夜白鯨現", "mobile_link":"https://m.douban.com/book/subject/1220562/", "summary":"那一年,是聽莫扎特、釣鱸魚和家庭破裂的一年。說到家庭破裂,母親怪自己當初沒有找到好男人,父親則認為當時是被狐狸精迷住了眼,失常的是母親,但出問題的是父親……。", "attrs":{ "publisher":["青島出版社"], "pubdate":["2005-01-01"], "author":["片山恭一", "豫人"], "price":["18.00元"], "title":["滿月之夜白鯨現"], "binding":["平裝(無盤)"], "translator":["豫人"], "pages":["180"] }, "tags":[ {"count":106, "name":"片山恭一"}, {"count":50, "name":"日本"}, {"count":42, "name":"日本文學"}, {"count":30, "name":"滿月之夜白鯨現"}, {"count":28, "name":"小說"}, {"count":10, "name":"愛情"}, {"count":7, "name":"純愛"}, {"count":6, "name":"外國文學"} ]}
這個和Python當中的字典是不是很像,然後我們將其轉換成字典,並取得相應的數據。下面我們來具體的看一下相應的代碼。
class GetBookInfo: def __init__(self, isbn): if isbn == "": self.isbn = "1234567890123" else: self.isbn = isbn def getbookinfo(self): """ 利用豆瓣API讀取圖書信息 """ url = "https://api.douban.com/v2/book/isbn/:" + self.isbn header = { "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36" } r = requests.get(url, headers = header) info = r.text bookinfo_dic = json.loads(info) bookinfo = {"subtitle" : "", "author": "", "pubdate" : "", "classification" : "", "publisher" : "", "price" : "", "pages" : "", "summary" : "", "img" : "", "country" : "" } if bookinfo_dic.get("code"): rstatus = "0" return rstatus, bookinfo else: rstatus = "1" subtitle = bookinfo_dic["title"] author = " ".join(bookinfo_dic["author"]) pubdate = bookinfo_dic["pubdate"] classification = bookinfo_dic["tags"][0]["title"] publisher = bookinfo_dic["publisher"] price = bookinfo_dic["price"] pages = bookinfo_dic["pages"] summary = bookinfo_dic["summary"] img = bookinfo_dic["images"]["small"].replace("\", "") if author[0] == "[": country = author[1] else: country = "中" bookinfo = {"subtitle" : subtitle, "author": author, "pubdate" : pubdate, "classification" : classification, "publisher" : publisher, "price" : price, "pages" : pages, "summary" : summary, "img" : img, "country" : country } return rstatus, bookinfo
為節約篇幅,主要把getbookinfo()函數講一下。
url = "https://api.douban.com/v2/book/isbn/:" + self.isbnheader = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36"}r = requests.get(url, headers = header)info = r.text
這幾句話是利用Python第三方庫requests以及豆瓣API獲取圖書信息,至於requests庫的使用,相應的教程百度上已經爛大街了,可以自己看看。這裡放上中文版的幫助文檔。
快速上手 - Requests 2.18.1 文檔
bookinfo_dic = json.loads(info)
將獲取的json數據轉換成字典格式,這樣Python就能操作了。它還有一個姊妹函數,如下:
json.dumps()#將 Python 對象編碼成 JSON 字元串bookinfo = {"subtitle" : "", "author": "", "pubdate" : "", "classification" : "", "publisher" : "", "price" : "", "pages" : "", "summary" : "", "img" : "", "country" : "" }
這個字典是用於存儲圖書信息的,分別對應圖書的名稱、作者、出版日期、分類、出版單位、價格、頁數、內容簡介、封面、作者國家。
if bookinfo_dic.get("code"): rstatus = "0" return rstatus, bookinfoelse: rstatus = "1" subtitle = bookinfo_dic["title"] author = " ".join(bookinfo_dic["author"]) pubdate = bookinfo_dic["pubdate"] classification = bookinfo_dic["tags"][0]["title"] publisher = bookinfo_dic["publisher"] price = bookinfo_dic["price"] pages = bookinfo_dic["pages"] summary = bookinfo_dic["summary"] img = bookinfo_dic["images"]["small"].replace("\", "") if author[0] == "[": country = author[1] else: country = "中" bookinfo = {"subtitle" : subtitle, "author": author, "pubdate" : pubdate, "classification" : classification, "publisher" : publisher, "price" : price, "pages" : pages, "summary" : summary, "img" : img, "country" : country } return rstatus, bookinfo
這裡做了一個判斷,如果我們讀取圖書數據的時候,其返回值包含code這個欄位,且code = 0,表明我們獲取數據失敗,可能是ISBN號不對等情況。否則將圖書信息存入bookinfo這個字典當中。這裡需要注意兩點:
1、國家信息是我自己增加的,API中沒有明確的顯示出來,注意是根據作者信息判斷出來的,國外的作者名稱前面都帶有[]符號,如下圖:
if author[0] == "[": country = author[1]else: country = "中"
2、封面的鏈接是需要修改的,不能直接拿來用,因為它是這樣的:
"https://img1.doubanio.com/view/subject/s/public/s29745409.jpg"
我們需要吧鏈接當中的「」替換成空字元串」」,所以有了如下的代碼:
img = bookinfo_dic["images"]["small"].replace("\", "")
數據存儲
所有的數據操作類是存放到DataManagement這個類當中的,我們所有的圖書檔案數據載入到數據內存中,是存儲到booklist這個列表變數。booklist這個列表變數中每一個元素是一個字典,即每本圖書的檔案信息,如:書名、作者、出版日期之類的。
在這裡我們選擇存儲的方式是Python的pickle模塊,為什麼選擇pickle模塊。
如果我們這樣操作:
list = [1,2,3]f = open("xx.dat","w")f.write(list)f.close()
執行即報錯:
Traceback (most recent call last): File "C:UsersAdministratorDesktop66666666.py", line 6, in <module> f.write(list)TypeError: write() argument must be str, not list
但是使用pickle就不會。
import picklelist = [1,2,3]f = open("xx.dat","wb")pickle.dump(list, f)f.close()
pickle能夠存儲列表,使用十分方便。
好吧,我們來看看相應的代碼。
class DataManagement: books = [] def insert_db(self, bookinfo): self.books = self.load() for book in self.books: if book["isbn"] == bookinfo["isbn"]: return -1 else: self.books.append(bookinfo) with codecs.open("book.dat", "wb") as f: pickle.dump(self.books, f) return 1 def save_db(self, bookinfoes): with codecs.open("book.dat", "wb") as f: pickle.dump(bookinfoes, f) def query_db(self, isbn = "",author = "",bookname = ""): self.books = self.load() if isbn: for i, book in enumerate(self.books): if book["isbn"] == isbn: return i else: return -1 if author: for i, book in enumerate(self.books): if book["author"] == author: return i else: return -1 if bookname: for i, book in enumerate(self.books): if book["subtitle"] == bookname: return i else: return -1 def load(self): pathname = "book.dat" if not(os.path.exists(pathname) and os.path.isfile(pathname)): with codecs.open("book.dat", "wb") as f: pickle.dump(self.books, f) with codecs.open("book.dat", "rb") as f: books = pickle.load(f) return books
這個類中我們建立幾個函數:
1、insert_db:插入一條圖書數據
2、save_db:保存所有圖書檔案
3、query_db:查找圖書
4、load:載入數據
def load(self): pathname = "book.dat" if not(os.path.exists(pathname) and os.path.isfile(pathname)): with codecs.open("book.dat", "wb") as f: pickle.dump(self.books, f) with codecs.open("book.dat", "rb") as f: books = pickle.load(f) return books
存儲數據之前先要載入數據。我們先判斷數據存儲的文件「book.dat」是否存在,不存在的話我們就新建一個,利用pickle.dump()函數新建。
然後我們打開「book.dat」,使用pickle.load()載入列表,並返回列表books。
有關pickle的文章,之前寫過一篇,不過是在Python2環境下,在Python3中已經沒有cPickle了,但是用法上大部分通用。可以參考:
《Python模塊學習:pickle/cPickle模塊》def insert_db(self, bookinfo): self.books = self.load() for book in self.books: if book["isbn"] == bookinfo["isbn"]: return -1 else: self.books.append(bookinfo) with codecs.open("book.dat", "wb") as f: pickle.dump(self.books, f) return 1
這個函數中參數bookinfo表示一條圖書檔案信息,即一本書的信息。
這裡我們做了一個判斷,如果插入的ISBN號是存在的返回-1。如果遍歷全部圖書信息都是不存在的,那麼,我們給self.books這一列表中新增一個圖書信息,並保存到「book.dat」中,同時返回1,表示保存沒有問題。有關for……else的用法,可以參考這篇文章:
《Python小知識:else除了跟if很搭,你還知道其他的關鍵詞嗎?》def query_db(self, isbn = "",author = "",bookname = ""): self.books = self.load() if isbn: for i, book in enumerate(self.books): if book["isbn"] == isbn: return i else: return -1 if author: for i, book in enumerate(self.books): if book["author"] == author: return i else: return -1 if bookname: for i, book in enumerate(self.books): if book["subtitle"] == bookname: return i else: return -1
這裡我們可以根據ISBN、作者、書名來查找圖書,要是找到了就返回這本書在self.books中的位置,如果沒有找到返回-1。
def save_db(self, bookinfoes): with codecs.open("book.dat", "wb") as f: pickle.dump(bookinfoes, f)
這個函數比較好理解,就是保存整個圖書檔案。這裡的bookinfoes是整個圖書檔案的列表。
最後
總結下我們今天的內容:
1、程序設計思路
2、豆瓣API使用
3、數據存儲
ok,今天的介紹就到這裡吧。下章我們繼續。如果你喜歡本篇文章,請給我點贊
讚賞(推薦)
分享給你的好友們吧!
關注微信公眾號:學點編程吧,可以獲得更多PyQt5的代碼!加油!(? ??_??)? (*?????)
推薦閱讀: