Build a Multi‑Book PDF Reader with Python, Qt, and SQLite
This article walks through the development of PyReadon, a Python‑based PDF reader that supports adding, deleting, reading, and tracking multiple books, with left‑click page turning and persistent storage using SQLite.
After two weeks of iteration, PyReadon now provides basic PDF reader functions such as adding, deleting, reading books, saving reading records, and viewing book information.
The latest version optimizes the book data structure, enabling simultaneous reading of multiple books, left‑click page turning, and interaction with an SQLite database to store library and book details (path, page, flag).
Book Class
The Book class stores metadata, page number, and reading status, and implements the __eq__ method to compare two book instances.
class Book:
def __init__(self, fname):
# filename
self.fname = fname
# reading flag
self.flag = None
self._info = None
self._page = 0
self.get_meta_data(self.fname)
def __eq__(self, other):
if hasattr(other, 'fname'):
return self.fname == other.fname
return FalseSimultaneous Reading
PyReadon maintains a read_list list that stores Book objects, each with a page attribute, allowing the program to remember the current page of each open book.
self.read_list = [None]
self.read_list.extend(book for book in self.booklist if book.flag)Left‑Click Page Turning
The mousePressEvent method of the custom MyArea widget captures mouse coordinates; clicking in the left third of the widget turns the page backward, while clicking in the right third turns it forward.
# Mouse left‑click page turning
def mousePressEvent(self, event):
pos = event.pos().x()
width = self.size().width()
if event.button() == Qt.LeftButton:
if pos > width * 2 / 3:
self.right()
elif pos < width / 3:
self.left()SQLite Integration
SQLite provides a lightweight, server‑less database. On startup, read_db creates PDF.db and a book_info table if they do not exist, then yields Book objects populated with stored data.
book_db = 'PDF.db'
book_info = namedtuple('info', 'path page flag')
def read_db():
os.chdir(os.path.dirname(os.path.realpath(__file__)))
if not os.path.exists(book_db):
conn = sqlite3.connect(book_db)
conn.execute("CREATE TABLE book_info(path, page, flag)")
conn.close()
conn = sqlite3.connect(book_db)
for row in conn.execute('SELECT * FROM book_info'):
info = book_info(*row)
book = Book(info.path)
book.page = info.page
book.flag = info.flag
yield book
conn.close()Saving data uses save2db, which inserts each book’s path, page, and flag into the database via executemany. The remove_db function clears the table before a new save.
def save2db(booklist):
conn = sqlite3.connect(book_db)
conn.executemany("INSERT INTO book_info Values (?,?,?)",
((book.fname, book.page, book.flag) for book in booklist))
conn.commit()
conn.close()
def remove_db():
conn = sqlite3.connect(book_db)
conn.execute('DELETE FROM book_info')
conn.commit()
conn.close()Viewing Book Information
Right‑clicking a book triggers a QMessageBox that displays its metadata (path, format, title, author, creator, producer).
elif action == item3:
index = row_num * 8 + col_num
book = self.booklist[index]
info = book.info
fmt = f'路径:{info.path}
' \
f'格式:{info.format}
' \
f'标题:{info.title}
' \
f'作者:{info.author}
' \
f'Creator:{info.creator}
' \
f'Producer:{info.producer}
'
QMessageBox.about(self, '文档信息', fmt)The complete source code is available on GitHub at https://github.com/cassieeric/Python-Application for further study.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Python Crawling & Data Mining
Life's short, I code in Python. This channel shares Python web crawling, data mining, analysis, processing, visualization, automated testing, DevOps, big data, AI, cloud computing, machine learning tools, resources, news, technical articles, tutorial videos and learning materials. Join us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
