Initial file loading
epub content parsing is horribly borked
This commit is contained in:
92
__main__.py
92
__main__.py
@@ -5,6 +5,7 @@
|
||||
Check files (hashes) upon restart
|
||||
Recursive file addition
|
||||
Show what on startup
|
||||
If cache large files
|
||||
Library:
|
||||
✓ sqlite3 for cover images cache
|
||||
✓ sqlite3 for storing metadata
|
||||
@@ -13,8 +14,9 @@
|
||||
✓ Image reflow
|
||||
✓ Search bar in toolbar
|
||||
✓ Shift focus to the tab that has the book open
|
||||
? Create emblem per filetype
|
||||
Look into how you might group icons
|
||||
Ignore a / the / numbers for sorting purposes
|
||||
Maybe create emblem per filetype
|
||||
Put the path in the scope of the search
|
||||
maybe as a type: switch
|
||||
Mass tagging
|
||||
@@ -26,17 +28,18 @@
|
||||
✓ Override the keypress event of the textedit
|
||||
✓ Use format* icons for toolbar buttons
|
||||
✓ Implement book view settings with a(nother) toolbar
|
||||
Consider substituting the textedit for another widget
|
||||
? Substitute textedit for another widget
|
||||
All ebooks should first be added to the database and then returned as HTML
|
||||
Pagination
|
||||
Theming
|
||||
Set context menu for definitions and the like
|
||||
Keep fontsize and margins consistent - Let page increase in length
|
||||
Filetypes:
|
||||
? Plugin system for parsers
|
||||
? pdf support
|
||||
epub support
|
||||
mobi, azw support
|
||||
txt, doc, djvu support
|
||||
pdf support?
|
||||
cbz, cbr support
|
||||
Keep font settings enabled but only for background color
|
||||
Internet:
|
||||
@@ -44,7 +47,7 @@
|
||||
Get ISBN using python-isbnlib
|
||||
Other:
|
||||
✓ Define every widget in code
|
||||
Maybe include icons for emblems
|
||||
? Include icons for emblems
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -91,6 +94,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
|
||||
self.bookToolBar = BookToolBar(self)
|
||||
self.bookToolBar.fullscreenButton.triggered.connect(self.set_fullscreen)
|
||||
self.bookToolBar.tocBox.activated.connect(self.set_toc_position)
|
||||
self.addToolBar(self.bookToolBar)
|
||||
|
||||
# Make the correct toolbar visible
|
||||
@@ -99,10 +103,12 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
|
||||
# New tabs and their contents
|
||||
self.current_tab = None
|
||||
self.current_textEdit = None
|
||||
self.current_contentView = None
|
||||
|
||||
# Tab closing
|
||||
self.tabWidget.setTabsClosable(True)
|
||||
# TODO
|
||||
# It's possible to add a widget to the Library tab here
|
||||
self.tabWidget.tabBar().setTabButton(0, QtWidgets.QTabBar.RightSide, None)
|
||||
self.tabWidget.tabCloseRequested.connect(self.close_tab)
|
||||
|
||||
@@ -162,7 +168,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
if my_file[0]:
|
||||
self.listView.setEnabled(False)
|
||||
self.last_open_path = os.path.dirname(my_file[0][0])
|
||||
books = sorter.BookSorter(my_file[0], self.database_path)
|
||||
books = sorter.BookSorter(my_file[0], 'addition', self.database_path)
|
||||
parsed_books = books.initiate_threads()
|
||||
database.DatabaseFunctions(self.database_path).add_to_database(parsed_books)
|
||||
self.listView.setEnabled(True)
|
||||
@@ -176,11 +182,14 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
if box_button.text() == '&Yes':
|
||||
selected_hashes = []
|
||||
for i in selected_books:
|
||||
book_data = i.data(QtCore.Qt.UserRole + 3)
|
||||
selected_hashes.append(book_data['book_hash'])
|
||||
data = i.data(QtCore.Qt.UserRole + 3)
|
||||
selected_hashes.append(data['hash'])
|
||||
database.DatabaseFunctions(
|
||||
self.database_path).delete_from_database(selected_hashes)
|
||||
self.viewModel = None
|
||||
self.viewModel = None # TODO
|
||||
# Delete the item from the model instead
|
||||
# of reconstructing it
|
||||
# The same goes for addition
|
||||
self.reload_listview()
|
||||
|
||||
selected_number = len(selected_books)
|
||||
@@ -205,8 +214,10 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
|
||||
def tab_switch(self):
|
||||
if self.tabWidget.currentIndex() == 0:
|
||||
|
||||
self.bookToolBar.hide()
|
||||
self.libraryToolBar.show()
|
||||
|
||||
if self.lib_ref.proxy_model:
|
||||
# Making the proxy model available doesn't affect
|
||||
# memory utilization at all. Bleh.
|
||||
@@ -215,51 +226,76 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
else:
|
||||
self.bookToolBar.show()
|
||||
self.libraryToolBar.hide()
|
||||
|
||||
current_metadata = self.tabWidget.widget(
|
||||
self.tabWidget.currentIndex()).book_metadata
|
||||
current_title = current_metadata['book_title']
|
||||
current_author = current_metadata['book_author']
|
||||
self.tabWidget.currentIndex()).metadata
|
||||
|
||||
current_title = current_metadata['title']
|
||||
current_author = current_metadata['author']
|
||||
current_position = current_metadata['position']
|
||||
current_toc = current_metadata['content'].keys()
|
||||
|
||||
self.bookToolBar.tocBox.blockSignals(True)
|
||||
self.bookToolBar.tocBox.clear()
|
||||
self.bookToolBar.tocBox.addItems(current_toc)
|
||||
if current_position:
|
||||
self.bookToolBar.tocBox.setCurrentIndex(current_position)
|
||||
self.bookToolBar.tocBox.blockSignals(False)
|
||||
|
||||
self.statusMessage.setText(
|
||||
current_author + ' - ' + current_title)
|
||||
|
||||
def set_toc_position(self, event=None):
|
||||
self.tabWidget.widget(
|
||||
self.tabWidget.currentIndex()).metadata[
|
||||
'position'] = event
|
||||
|
||||
chapter_name = self.bookToolBar.tocBox.currentText()
|
||||
|
||||
current_tab = self.tabWidget.widget(self.tabWidget.currentIndex())
|
||||
required_content = current_tab.metadata['content'][chapter_name]
|
||||
current_tab.contentView.setHtml(required_content)
|
||||
|
||||
def set_fullscreen(self):
|
||||
self.current_tab = self.tabWidget.currentIndex()
|
||||
self.current_textEdit = self.tabWidget.widget(self.current_tab)
|
||||
self.current_contentView = self.tabWidget.widget(self.current_tab)
|
||||
|
||||
self.exit_shortcut = QtWidgets.QShortcut(
|
||||
QtGui.QKeySequence('Escape'), self.current_textEdit)
|
||||
QtGui.QKeySequence('Escape'), self.current_contentView)
|
||||
self.exit_shortcut.activated.connect(self.set_normalsize)
|
||||
|
||||
self.current_textEdit.setWindowFlags(QtCore.Qt.Window)
|
||||
self.current_textEdit.setWindowState(QtCore.Qt.WindowFullScreen)
|
||||
self.current_contentView.setWindowFlags(QtCore.Qt.Window)
|
||||
self.current_contentView.setWindowState(QtCore.Qt.WindowFullScreen)
|
||||
self.hide()
|
||||
self.current_textEdit.show()
|
||||
self.current_contentView.show()
|
||||
|
||||
def set_normalsize(self):
|
||||
self.current_textEdit.setWindowState(QtCore.Qt.WindowNoState)
|
||||
self.current_textEdit.setWindowFlags(QtCore.Qt.Widget)
|
||||
self.current_contentView.setWindowState(QtCore.Qt.WindowNoState)
|
||||
self.current_contentView.setWindowFlags(QtCore.Qt.Widget)
|
||||
self.show()
|
||||
self.current_textEdit.show()
|
||||
self.current_contentView.show()
|
||||
|
||||
def list_doubleclick(self, myindex):
|
||||
# TODO
|
||||
# Load the book.
|
||||
index = self.listView.model().index(myindex.row(), 0)
|
||||
book_metadata = self.listView.model().data(index, QtCore.Qt.UserRole + 3)
|
||||
metadata = self.listView.model().data(index, QtCore.Qt.UserRole + 3)
|
||||
|
||||
# Shift focus to the tab that has the book open (if there is one)
|
||||
for i in range(1, self.tabWidget.count()):
|
||||
tab_book_metadata = self.tabWidget.widget(i).book_metadata
|
||||
if tab_book_metadata['book_hash'] == book_metadata['book_hash']:
|
||||
tab_metadata = self.tabWidget.widget(i).metadata
|
||||
if tab_metadata['hash'] == metadata['hash']:
|
||||
self.tabWidget.setCurrentIndex(i)
|
||||
return
|
||||
|
||||
tab_ref = Tab(book_metadata, self.tabWidget)
|
||||
path = metadata['path']
|
||||
contents = sorter.BookSorter(
|
||||
[path], 'reading', self.database_path).initiate_threads()
|
||||
|
||||
tab_ref = Tab(contents, self.tabWidget)
|
||||
self.tabWidget.setCurrentWidget(tab_ref)
|
||||
print(tab_ref.book_metadata) # Metadata upon tab creation
|
||||
# print(tab_ref.book_metadata) # Metadata upon tab creation
|
||||
|
||||
def close_tab(self, tab_index):
|
||||
print(self.tabWidget.widget(tab_index).book_metadata) # Metadata upon tab deletion
|
||||
# print(self.tabWidget.widget(tab_index).metadata) # Metadata upon tab deletion
|
||||
self.tabWidget.removeTab(tab_index)
|
||||
|
||||
def closeEvent(self, event=None):
|
||||
|
40
database.py
40
database.py
@@ -3,6 +3,7 @@
|
||||
import sqlite3
|
||||
import os
|
||||
|
||||
|
||||
class DatabaseInit:
|
||||
def __init__(self, location_prefix):
|
||||
os.makedirs(location_prefix, exist_ok=True)
|
||||
@@ -16,7 +17,7 @@ class DatabaseInit:
|
||||
self.database.execute(
|
||||
"CREATE TABLE books \
|
||||
(id INTEGER PRIMARY KEY, Title TEXT, Author TEXT, Year INTEGER, \
|
||||
Path TEXT, ISBN TEXT, Tags TEXT, Hash TEXT, CoverImage BLOB)")
|
||||
Path TEXT, Position TEXT, ISBN TEXT, Tags TEXT, Hash TEXT, CoverImage BLOB)")
|
||||
self.database.execute(
|
||||
"CREATE TABLE cache \
|
||||
(id INTEGER PRIMARY KEY, Name TEXT, Path TEXT, CachedDict BLOB)")
|
||||
@@ -26,28 +27,29 @@ class DatabaseInit:
|
||||
self.database.commit()
|
||||
self.database.close()
|
||||
|
||||
|
||||
class DatabaseFunctions:
|
||||
def __init__(self, location_prefix):
|
||||
database_path = os.path.join(location_prefix, 'Lector.db')
|
||||
self.database = sqlite3.connect(database_path)
|
||||
|
||||
def add_to_database(self, book_data):
|
||||
# book_data is expected to be a dictionary
|
||||
def add_to_database(self, data):
|
||||
# data is expected to be a dictionary
|
||||
# with keys corresponding to the book hash
|
||||
# and corresponding items containing
|
||||
# whatever else needs insertion
|
||||
# Haha I said insertion
|
||||
|
||||
for i in book_data.items():
|
||||
for i in data.items():
|
||||
book_hash = i[0]
|
||||
book_title = i[1]['title']
|
||||
book_author = i[1]['author']
|
||||
book_year = i[1]['year']
|
||||
if not book_year:
|
||||
book_year = 9999
|
||||
book_path = i[1]['path']
|
||||
book_cover = i[1]['cover_image']
|
||||
book_isbn = i[1]['isbn']
|
||||
title = i[1]['title']
|
||||
author = i[1]['author']
|
||||
year = i[1]['year']
|
||||
if not year:
|
||||
year = 9999
|
||||
path = i[1]['path']
|
||||
cover = i[1]['cover_image']
|
||||
isbn = i[1]['isbn']
|
||||
|
||||
sql_command_add = (
|
||||
"INSERT INTO books (Title,Author,Year,Path,ISBN,Hash,CoverImage) VALUES(?, ?, ?, ?, ?, ?, ?)")
|
||||
@@ -55,11 +57,11 @@ class DatabaseFunctions:
|
||||
# TODO
|
||||
# This is a placeholder. You will need to generate book covers
|
||||
# in case none are found
|
||||
if book_cover:
|
||||
if cover:
|
||||
self.database.execute(
|
||||
sql_command_add,
|
||||
[book_title, book_author, book_year,
|
||||
book_path, book_isbn, book_hash, sqlite3.Binary(book_cover)])
|
||||
[title, author, year,
|
||||
path, isbn, book_hash, sqlite3.Binary(cover)])
|
||||
|
||||
self.database.commit()
|
||||
|
||||
@@ -95,15 +97,15 @@ class DatabaseFunctions:
|
||||
sql_command_fetch = sql_command_fetch[:-3] # Truncate the last OR
|
||||
|
||||
# book data is returned as a list of tuples
|
||||
book_data = self.database.execute(sql_command_fetch).fetchall()
|
||||
data = self.database.execute(sql_command_fetch).fetchall()
|
||||
|
||||
if book_data:
|
||||
if data:
|
||||
# Because this is the result of a fetchall(), we need an
|
||||
# ugly hack (tm) to get correct results
|
||||
if fetch_one:
|
||||
return book_data[0][0]
|
||||
return data[0][0]
|
||||
|
||||
return book_data
|
||||
return data
|
||||
else:
|
||||
return None
|
||||
|
||||
|
@@ -10,6 +10,7 @@
|
||||
|
||||
import os
|
||||
import re
|
||||
import collections
|
||||
|
||||
import ebooklib.epub
|
||||
|
||||
@@ -24,7 +25,7 @@ class ParseEPUB:
|
||||
def read_book(self):
|
||||
try:
|
||||
self.book = ebooklib.epub.read_epub(self.filename)
|
||||
except (KeyError, AttributeError):
|
||||
except (KeyError, AttributeError, FileNotFoundError):
|
||||
print('Cannot parse ' + self.filename)
|
||||
return
|
||||
|
||||
@@ -100,3 +101,40 @@ class ParseEPUB:
|
||||
return isbn
|
||||
except KeyError:
|
||||
return
|
||||
|
||||
def get_contents(self):
|
||||
contents = collections.OrderedDict()
|
||||
|
||||
def flatten_chapter(toc_element):
|
||||
output_list = []
|
||||
for i in toc_element:
|
||||
if isinstance(i, (tuple, list)):
|
||||
output_list.extend(flatten_chapter(i))
|
||||
else:
|
||||
output_list.append(i)
|
||||
return output_list
|
||||
|
||||
for i in self.book.toc:
|
||||
if isinstance(i, (tuple, list)):
|
||||
title = i[0].title
|
||||
contents[title] = 'Composite Chapter'
|
||||
# composite_chapter = flatten_chapter(i)
|
||||
# composite_chapter_content = []
|
||||
# for j in composite_chapter:
|
||||
# href = j.href
|
||||
# composite_chapter_content.append(
|
||||
# self.book.get_item_with_href(href).get_content())
|
||||
|
||||
# contents[title] = composite_chapter_content
|
||||
else:
|
||||
title = i.title
|
||||
href = i.href
|
||||
try:
|
||||
content = self.book.get_item_with_href(href).get_content()
|
||||
if content:
|
||||
contents[title] = content.decode()
|
||||
else:
|
||||
raise AttributeError
|
||||
except AttributeError:
|
||||
contents[title] = ''
|
||||
return contents
|
||||
|
72
sorter.py
72
sorter.py
@@ -1,16 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# TODO
|
||||
# Methods that return None must be quantified here if needed
|
||||
# Methods that return None must be quantified within the parsing module
|
||||
# See if tags can be generated from book content
|
||||
# See if you want to include a hash of the book's name and author
|
||||
|
||||
import os
|
||||
import hashlib
|
||||
from multiprocessing.dummy import Pool
|
||||
|
||||
import database
|
||||
from parsers.epub import ParseEPUB
|
||||
|
||||
|
||||
class BookSorter:
|
||||
def __init__(self, file_list, database_path):
|
||||
def __init__(self, file_list, mode, database_path):
|
||||
# Have the GUI pass a list of files straight to here
|
||||
# Then, on the basis of what is needed, pass the
|
||||
# filenames to the requisite functions
|
||||
@@ -21,7 +25,9 @@ class BookSorter:
|
||||
self.all_books = {}
|
||||
self.database_path = database_path
|
||||
self.hashes = []
|
||||
self.database_hashes()
|
||||
self.mode = mode
|
||||
if database_path:
|
||||
self.database_hashes()
|
||||
|
||||
def database_hashes(self):
|
||||
all_hashes = database.DatabaseFunctions(
|
||||
@@ -34,6 +40,16 @@ class BookSorter:
|
||||
if all_hashes:
|
||||
self.hashes = [i[0] for i in all_hashes]
|
||||
|
||||
def database_position(self, file_hash):
|
||||
position = database.DatabaseFunctions(
|
||||
self.database_path).fetch_data(
|
||||
('Position',),
|
||||
'books',
|
||||
{'Hash': file_hash},
|
||||
'EQUALS',
|
||||
True)
|
||||
return position
|
||||
|
||||
def read_book(self, filename):
|
||||
# filename is expected as a string containg the
|
||||
# full path of the ebook file
|
||||
@@ -41,17 +57,21 @@ class BookSorter:
|
||||
with open(filename, 'rb') as current_book:
|
||||
file_md5 = hashlib.md5(current_book.read()).hexdigest()
|
||||
|
||||
# IF the file is NOT being loaded into the reader,
|
||||
# Do not allow addition in case the file is dupicated in the directory
|
||||
# OR is already in the database
|
||||
# TODO
|
||||
# See if you want to include a hash of the book's name and author
|
||||
if file_md5 in self.all_books.items() or file_md5 in self.hashes:
|
||||
# This should not get triggered in reading mode
|
||||
if (self.mode == 'addition'
|
||||
and (file_md5 in self.all_books.items() or file_md5 in self.hashes)):
|
||||
return
|
||||
|
||||
# TODO
|
||||
# See if tags can be generated from book content
|
||||
# Sort according to to file extension here
|
||||
book_ref = ParseEPUB(filename)
|
||||
# Select sorter by file extension
|
||||
try:
|
||||
file_extension = os.path.splitext(filename)[1][1:]
|
||||
if file_extension == 'epub':
|
||||
book_ref = ParseEPUB(filename)
|
||||
except IndexError:
|
||||
return
|
||||
|
||||
# Everything following this is standard
|
||||
# Some of the None returns will have to have
|
||||
@@ -61,16 +81,32 @@ class BookSorter:
|
||||
title = book_ref.get_title()
|
||||
author = book_ref.get_author()
|
||||
year = book_ref.get_year()
|
||||
cover_image = book_ref.get_cover_image()
|
||||
isbn = book_ref.get_isbn()
|
||||
|
||||
self.all_books[file_md5] = {
|
||||
'title': title,
|
||||
'author': author,
|
||||
'year': year,
|
||||
'isbn': isbn,
|
||||
'path': filename,
|
||||
'cover_image': cover_image}
|
||||
# Different modes require different values
|
||||
if self.mode == 'addition':
|
||||
cover_image = book_ref.get_cover_image()
|
||||
self.all_books[file_md5] = {
|
||||
'title': title,
|
||||
'author': author,
|
||||
'year': year,
|
||||
'isbn': isbn,
|
||||
'path': filename,
|
||||
'cover_image': cover_image}
|
||||
|
||||
if self.mode == 'reading':
|
||||
content = book_ref.get_contents()
|
||||
position = self.database_position(file_md5)
|
||||
self.all_books = {
|
||||
'title': title,
|
||||
'author': author,
|
||||
'year': year,
|
||||
'isbn': isbn,
|
||||
'hash': file_md5,
|
||||
'path': filename,
|
||||
'position': position,
|
||||
'content': content}
|
||||
|
||||
|
||||
def initiate_threads(self):
|
||||
_pool = Pool(5)
|
||||
|
@@ -31,51 +31,52 @@ class Library:
|
||||
for i in books:
|
||||
# The database query returns a tuple with the following indices
|
||||
# Index 0 is the key ID is ignored
|
||||
book_title = i[1]
|
||||
book_author = i[2]
|
||||
book_year = i[3]
|
||||
book_cover = i[8]
|
||||
book_tags = i[6]
|
||||
book_path = i[4]
|
||||
book_progress = None # TODO
|
||||
# Leave at None for an untouched book
|
||||
# 'completed' for a completed book
|
||||
# whatever else is here can be used
|
||||
# to remember position
|
||||
title = i[1]
|
||||
author = i[2]
|
||||
year = i[3]
|
||||
path = i[4]
|
||||
tags = i[6]
|
||||
cover = i[9]
|
||||
progress = None # TODO
|
||||
# Leave at None for an untouched book
|
||||
# 'completed' for a completed book
|
||||
# whatever else is here can be used
|
||||
# to remember position
|
||||
# Maybe get from the position param
|
||||
|
||||
all_metadata = {
|
||||
'book_title': i[1],
|
||||
'book_author': i[2],
|
||||
'book_year': i[3],
|
||||
'book_path': i[4],
|
||||
'book_isbn': i[5],
|
||||
'book_tags': i[6],
|
||||
'book_hash': i[7]}
|
||||
'title': i[1],
|
||||
'author': i[2],
|
||||
'year': i[3],
|
||||
'path': i[4],
|
||||
'position': i[5],
|
||||
'isbn': i[6],
|
||||
'tags': i[7],
|
||||
'hash': i[8]}
|
||||
|
||||
tooltip_string = book_title + '\nAuthor: ' + book_author + '\nYear: ' + str(book_year)
|
||||
if book_tags:
|
||||
tooltip_string += ('\nTags: ' + book_tags)
|
||||
tooltip_string = title + '\nAuthor: ' + author + '\nYear: ' + str(year)
|
||||
if tags:
|
||||
tooltip_string += ('\nTags: ' + tags)
|
||||
|
||||
# This remarkably ugly hack is because the QSortFilterProxyModel
|
||||
# doesn't easily allow searching through multiple item roles
|
||||
search_workaround = book_title + ' ' + book_author
|
||||
if book_tags:
|
||||
search_workaround += book_tags
|
||||
search_workaround = title + ' ' + author
|
||||
if tags:
|
||||
search_workaround += tags
|
||||
|
||||
# Generate book state for passing onto the QStyledItemDelegate
|
||||
def generate_book_state(book_path, book_progress):
|
||||
if not os.path.exists(book_path):
|
||||
def generate_book_state(path, progress):
|
||||
if not os.path.exists(path):
|
||||
return 'deleted'
|
||||
|
||||
if book_progress:
|
||||
if book_progress == 'completed':
|
||||
if progress:
|
||||
if progress == 'completed':
|
||||
return 'completed'
|
||||
else:
|
||||
return 'inprogress'
|
||||
else:
|
||||
return None
|
||||
|
||||
book_state = generate_book_state(book_path, book_progress)
|
||||
state = generate_book_state(path, progress)
|
||||
|
||||
# Generate image pixmap and then pass it to the widget
|
||||
# as a QIcon
|
||||
@@ -84,17 +85,17 @@ class Library:
|
||||
# QtCore.Qt.DisplayRole is the same as item.setText()
|
||||
# The model is a single row and has no columns
|
||||
img_pixmap = QtGui.QPixmap()
|
||||
img_pixmap.loadFromData(book_cover)
|
||||
img_pixmap.loadFromData(cover)
|
||||
img_pixmap = img_pixmap.scaled(420, 600, QtCore.Qt.IgnoreAspectRatio)
|
||||
item = QtGui.QStandardItem()
|
||||
item.setToolTip(tooltip_string)
|
||||
# The following order is needed to keep sorting working
|
||||
item.setData(book_title, QtCore.Qt.UserRole)
|
||||
item.setData(book_author, QtCore.Qt.UserRole + 1)
|
||||
item.setData(book_year, QtCore.Qt.UserRole + 2)
|
||||
item.setData(title, QtCore.Qt.UserRole)
|
||||
item.setData(author, QtCore.Qt.UserRole + 1)
|
||||
item.setData(year, QtCore.Qt.UserRole + 2)
|
||||
item.setData(all_metadata, QtCore.Qt.UserRole + 3)
|
||||
item.setData(search_workaround, QtCore.Qt.UserRole + 4)
|
||||
item.setData(book_state, QtCore.Qt.UserRole + 5)
|
||||
item.setData(state, QtCore.Qt.UserRole + 5)
|
||||
item.setIcon(QtGui.QIcon(img_pixmap))
|
||||
self.parent_window.viewModel.appendRow(item)
|
||||
|
||||
@@ -108,7 +109,8 @@ class Library:
|
||||
def update_proxymodel(self):
|
||||
self.proxy_model.setFilterRole(QtCore.Qt.UserRole + 4)
|
||||
self.proxy_model.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
|
||||
self.proxy_model.setFilterWildcard(self.parent_window.libraryToolBar.filterEdit.text())
|
||||
self.proxy_model.setFilterWildcard(
|
||||
self.parent_window.libraryToolBar.filterEdit.text())
|
||||
|
||||
self.parent_window.statusMessage.setText(
|
||||
str(self.proxy_model.rowCount()) + ' books')
|
||||
|
38
widgets.py
38
widgets.py
@@ -97,9 +97,7 @@ class BookToolBar(QtWidgets.QToolBar):
|
||||
self.searchBar.setObjectName('searchBar')
|
||||
|
||||
# Sorter
|
||||
sorting_choices = ['Chapter ' + str(i) for i in range(1, 11)]
|
||||
self.tocBox = QtWidgets.QComboBox()
|
||||
self.tocBox.addItems(sorting_choices)
|
||||
self.tocBox.setObjectName('sortingBox')
|
||||
self.tocBox.setSizePolicy(sizePolicy)
|
||||
self.tocBox.setMinimumContentsLength(10)
|
||||
@@ -216,27 +214,31 @@ class LibraryToolBar(QtWidgets.QToolBar):
|
||||
|
||||
|
||||
class Tab(QtWidgets.QWidget):
|
||||
def __init__(self, book_metadata, parent=None):
|
||||
def __init__(self, metadata, parent=None):
|
||||
# TODO
|
||||
# The display widget will probably have to be shifted to something else
|
||||
# A horizontal slider to control flow
|
||||
# Keyboard shortcuts
|
||||
|
||||
# The content display widget is currently a QTextBrowser
|
||||
super(Tab, self).__init__(parent)
|
||||
self.parent = parent
|
||||
self.book_metadata = book_metadata # Save progress data into this dictionary
|
||||
self.metadata = metadata # Save progress data into this dictionary
|
||||
self.setStyleSheet("background-color: black")
|
||||
|
||||
book_title = self.book_metadata['book_title']
|
||||
book_path = self.book_metadata['book_path']
|
||||
title = self.metadata['title']
|
||||
path = self.metadata['path']
|
||||
|
||||
self.gridLayout = QtWidgets.QGridLayout(self)
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
self.textEdit = QtWidgets.QTextEdit(self)
|
||||
self.textEdit.setObjectName("textEdit")
|
||||
self.textEdit.setFrameShape(QtWidgets.QFrame.NoFrame)
|
||||
self.gridLayout.addWidget(self.textEdit, 0, 0, 1, 1)
|
||||
self.parent.addTab(self, book_title)
|
||||
self.textEdit.setText(book_path)
|
||||
self.contentView = QtWidgets.QTextBrowser(self)
|
||||
self.contentView.setFrameShape(QtWidgets.QFrame.NoFrame)
|
||||
self.contentView.setObjectName("contentView")
|
||||
self.contentView.verticalScrollBar().setSingleStep(7)
|
||||
self.contentView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||
self.gridLayout.addWidget(self.contentView, 0, 0, 1, 1)
|
||||
self.parent.addTab(self, title)
|
||||
self.contentView.setStyleSheet(
|
||||
"QTextEdit {font-size:20px; padding-left:100; padding-right:100; background-color:black}")
|
||||
|
||||
|
||||
class LibraryDelegate(QtWidgets.QStyledItemDelegate):
|
||||
@@ -246,13 +248,13 @@ class LibraryDelegate(QtWidgets.QStyledItemDelegate):
|
||||
def paint(self, painter, option, index):
|
||||
QtWidgets.QStyledItemDelegate.paint(self, painter, option, index)
|
||||
option = option.__class__(option)
|
||||
book_state = index.data(QtCore.Qt.UserRole + 5)
|
||||
if book_state:
|
||||
if book_state == 'deleted':
|
||||
state = index.data(QtCore.Qt.UserRole + 5)
|
||||
if state:
|
||||
if state == 'deleted':
|
||||
read_icon = QtGui.QIcon.fromTheme('vcs-conflicting').pixmap(36)
|
||||
if book_state == 'completed':
|
||||
if state == 'completed':
|
||||
read_icon = QtGui.QIcon.fromTheme('vcs-normal').pixmap(36)
|
||||
if book_state == 'inprogress':
|
||||
if state == 'inprogress':
|
||||
read_icon = QtGui.QIcon.fromTheme('vcs-locally-modified').pixmap(36)
|
||||
else:
|
||||
return
|
||||
|
Reference in New Issue
Block a user