標籤:

PyQt5系列教程(47):極簡圖書管理(QTableWidget的使用)2

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的文檔如下:developers.douban.com/w

有興趣的學友可以自己去看看,內容也比較豐富。

我們這裡主要是使用讀書API:developers.douban.com/w

這個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 文檔?

docs.python-requests.org

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模塊》?

mp.weixin.qq.com

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很搭,你還知道其他的關鍵詞嗎?》?

mp.weixin.qq.com

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的代碼!加油!(? ??_??)? (*?????)

推薦閱讀:

TAG:PyQt | PyQt5 | | Python |