Make progress work with block count

Break database thoroughly
Fix pdf year bug
This commit is contained in:
BasioMeusPuga
2018-03-31 03:03:49 +05:30
parent 0b8427c864
commit aff69d95c1
8 changed files with 151 additions and 141 deletions

10
TODO
View File

@@ -59,7 +59,10 @@ TODO
✓ Comic view context menu
✓ Make the bookmark dock float over the reading area
✓ Spacebar should not cut off lines at the top
Track open bookmark windows so they can be closed quickly at exit
Track open bookmark windows so they can be closed quickly at exit
Annotations
Text
Image
Adjust key navigation according to viewport dimensions
Search document using QTextCursor
Filetypes:
@@ -76,14 +79,13 @@ TODO
✓ Define every widget in code
Bugs:
Deselecting all directories in the settings dialog also filters out manually added books
Only one tab has its scroll position set when opening multiple books @ startup
It's the one that has focus when application starts and ends
PDF year
Secondary:
Annotations
Graphical themes
Change focus rectangle dimensions
Tab reordering
Universal Ctrl + Tab
Allow tabs to detach and form their own windows
Goodreads API: Ratings, Read, Recommendations
Get ISBN using python-isbnlib

View File

@@ -553,7 +553,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
self.bookToolBar.tocBox.setCurrentIndex(
current_position['current_chapter'] - 1)
if not current_metadata['images_only']:
current_tab.set_cursor_position()
current_tab.hiddenButton.animateClick(25)
self.bookToolBar.tocBox.blockSignals(False)
self.profile_functions.format_contentView()
@@ -582,45 +582,15 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
def set_toc_position(self, event=None):
current_tab = self.tabWidget.widget(self.tabWidget.currentIndex())
# We're updating the underlying model to have real-time
# updates on the read status
# Set a baseline model index in case the item gets deleted
# E.g It's open in a tab and deleted from the library
model_index = None
start_index = self.lib_ref.view_model.index(0, 0)
# Find index of the model item that corresponds to the tab
matching_item = self.lib_ref.view_model.match(
start_index,
QtCore.Qt.UserRole + 6,
current_tab.metadata['hash'],
1, QtCore.Qt.MatchExactly)
if matching_item:
model_row = matching_item[0].row()
model_index = self.lib_ref.view_model.index(model_row, 0)
current_tab.metadata[
'position']['current_chapter'] = event + 1
current_tab.metadata[
'position']['is_read'] = False
# TODO
# This doesn't update correctly
# try:
# position_perc = (
# current_tab.metadata[
# 'current_chapter'] * 100 / current_tab.metadata['total_chapters'])
# except KeyError:
# position_perc = None
if model_index:
self.lib_ref.view_model.setData(
model_index, current_tab.metadata, QtCore.Qt.UserRole + 3)
# self.lib_ref.view_model.setData(
# model_index, position_perc, QtCore.Qt.UserRole + 7)
# Go on to change the value of the Table of Contents box
current_tab.change_chapter_tocBox()
current_tab.contentView.record_position()
self.profile_functions.format_contentView()
def set_fullscreen(self):

View File

@@ -53,7 +53,7 @@ class DatabaseInit:
'CheckState': 'INTEGER'}
if os.path.exists(self.database_path):
self.check_database()
self.check_columns()
else:
self.create_database()
@@ -72,7 +72,7 @@ class DatabaseInit:
self.database.commit()
self.database.close()
def check_database(self):
def check_columns(self):
self.database = sqlite3.connect(self.database_path)
database_return = self.database.execute("PRAGMA table_info(books)").fetchall()
@@ -89,7 +89,6 @@ class DatabaseInit:
if commit_required:
self.database.commit()
self.database.close()
class DatabaseFunctions:

View File

@@ -33,6 +33,7 @@ class LibraryDelegate(QtWidgets.QStyledItemDelegate):
# painter.fillRect(option.rect, QtGui.QColor().fromRgb(255, 0, 0, 20))
option = option.__class__(option)
title = index.data(QtCore.Qt.UserRole)
file_exists = index.data(QtCore.Qt.UserRole + 5)
metadata = index.data(QtCore.Qt.UserRole + 3)
@@ -65,21 +66,28 @@ class LibraryDelegate(QtWidgets.QStyledItemDelegate):
QtWidgets.QStyledItemDelegate.paint(self, painter, option, index)
if position:
if is_read:
current_chapter = total_chapters = 100
progress = total = -1
else:
try:
current_chapter = position['current_chapter']
total_chapters = position['total_chapters']
progress = position['current_block']
total = position['total_blocks']
if progress == total == 0:
raise KeyError
except KeyError:
return
# For comics and older database entries
# It looks ugly but leave it like this
try:
progress = position['current_chapter']
total = position['total_chapters']
except KeyError:
return
read_icon = pie_chart.pixmapper(
current_chapter, total_chapters, self.temp_dir, 36)
progress, total, self.temp_dir, 36)
x_draw = option.rect.bottomRight().x() - 30
y_draw = option.rect.bottomRight().y() - 35
if current_chapter != 1:
painter.drawPixmap(x_draw, y_draw, read_icon)
painter.drawPixmap(x_draw, y_draw, read_icon)
class BookmarkDelegate(QtWidgets.QStyledItemDelegate):

View File

@@ -122,6 +122,8 @@ class TableProxyModel(QtCore.QSortFilterProxyModel):
file_exists = item.data(QtCore.Qt.UserRole + 5)
metadata = item.data(QtCore.Qt.UserRole + 3)
progress_perc = item.data(QtCore.Qt.UserRole + 7)
position = metadata['position']
if position:
is_read = position['is_read']
@@ -132,27 +134,29 @@ class TableProxyModel(QtCore.QSortFilterProxyModel):
if position:
if is_read:
current_chapter = total_chapters = 100
progress = total = -2
else:
try:
current_chapter = position['current_chapter']
total_chapters = position['total_chapters']
progress = position['current_block']
total = position['total_blocks']
# TODO
# See if there's any rationale for this
if current_chapter == 1:
if progress == total == 0:
raise KeyError
except KeyError:
return
try:
progress = position['current_chapter']
total = position['total_chapters']
except KeyError:
return
return_pixmap = pie_chart.pixmapper(
current_chapter, total_chapters, self.temp_dir,
progress, total, self.temp_dir,
QtCore.Qt.SizeHintRole + 10)
return return_pixmap
elif role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
if index.column() in (0, 5): # Cover and Status
if index.column() in (0, 5): # Cover and Status
return QtCore.QVariant()
if index.column() == 4:

View File

@@ -63,8 +63,8 @@ class ParsePDF:
def get_year(self):
try:
year = self.metadata.find('MetadataDate').text
return year.replace('\n', '')
except AttributeError:
return int(year.replace('\n', '')[:4])
except (AttributeError, ValueError):
return 9999
def get_cover_image(self):

View File

@@ -94,25 +94,24 @@ def generate_pie(progress_percent, temp_dir=None):
return lSvg
def pixmapper(current_chapter, total_chapters, temp_dir, size):
def pixmapper(progress, total, temp_dir, size):
# A current_chapter of -1 implies the files does not exist
# A chapter number == Total chapters implies the file is unread
return_pixmap = None
if current_chapter == -1:
if progress == -1:
return_pixmap = QtGui.QIcon(':/images/error.svg').pixmap(size)
return return_pixmap
if current_chapter == total_chapters:
if progress >= .95 * total: # Consider book read @ 95% progress
return_pixmap = QtGui.QIcon(':/images/checkmark.svg').pixmap(size)
else:
# TODO
# See if saving the svg to disk can be avoided
# Shift to lines to track progress
# Maybe make the alignment a little more uniform across emblems
progress_percent = int(current_chapter * 100 / total_chapters)
progress_percent = int(progress * 100 / total)
generate_pie(progress_percent, temp_dir)
svg_path = os.path.join(temp_dir, 'lector_progress.svg')
return_pixmap = QtGui.QIcon(svg_path).pixmap(size - 4) ## The -4 looks more proportional

View File

@@ -20,7 +20,6 @@
# Reading modes
# Double page, Continuous etc
# Especially for comics
# Remove variables that have anything to do with scroll position
import os
@@ -191,57 +190,34 @@ class Tab(QtWidgets.QWidget):
except IndexError: # The file has been deleted
pass
def set_cursor_position(self, switch_widgets=True, search_data=None):
# if self.sender().objectName() == 'tabWidget' and self.first_run:
# return
# self.first_run = False
# ^^^ I have NO IDEA why this might be needed or how it works
if switch_widgets:
previous_widget = self.main_window.tabWidget.currentWidget()
self.main_window.tabWidget.setCurrentWidget(self)
def set_cursor_position(self, cursor_position=None):
try:
required_position = self.metadata['position']['cursor_position']
if search_data:
required_position = search_data[1]
# TODO
# Remove this at the time when a library rebuild is
# finally required
# Bookmarks will have to be regenerated in the meantime
if isinstance(required_position, str):
return
# This is needed so that the line we want is
# always at the top of the window
self.contentView.verticalScrollBar().setValue(
self.contentView.verticalScrollBar().maximum())
# textCursor() RETURNS a copy of the textcursor
cursor = self.contentView.textCursor()
cursor.setPosition(
required_position, QtGui.QTextCursor.MoveAnchor)
self.contentView.setTextCursor(cursor)
self.contentView.ensureCursorVisible()
except KeyError:
pass
print(f'Database: Cursor position error. Recommend retry.')
return
if switch_widgets:
self.main_window.tabWidget.setCurrentWidget(previous_widget)
if cursor_position:
required_position = cursor_position
# This is needed so that the line we want is
# always at the top of the window
self.contentView.verticalScrollBar().setValue(
self.contentView.verticalScrollBar().maximum())
# textCursor() RETURNS a copy of the textcursor
cursor = self.contentView.textCursor()
cursor.setPosition(
required_position, QtGui.QTextCursor.MoveAnchor)
self.contentView.setTextCursor(cursor)
self.contentView.ensureCursorVisible()
def generate_position(self, is_read=False):
total_chapters = len(self.metadata['content'])
current_chapter = 1
scroll_value = 0
if is_read:
current_chapter = total_chapters
scroll_value = 1
# TODO
# Use this to generate position
# Generate block count @ time of first read
# Blocks are indexed from 0 up
@@ -264,8 +240,8 @@ class Tab(QtWidgets.QWidget):
'total_chapters': total_chapters,
'blocks_per_chapter': blocks_per_chapter,
'total_blocks': total_blocks,
'scroll_value': scroll_value,
'is_read': is_read,
'current_block': 0,
'cursor_position': 0}
def generate_keyboard_shortcuts(self):
@@ -298,7 +274,7 @@ class Tab(QtWidgets.QWidget):
return
if not self.are_we_doing_images_only:
self.contentView.record_scroll_position()
self.contentView.record_position()
self.contentView.setWindowFlags(QtCore.Qt.Window)
self.contentView.setWindowState(QtCore.Qt.WindowFullScreen)
@@ -316,7 +292,7 @@ class Tab(QtWidgets.QWidget):
return
if not self.are_we_doing_images_only:
self.contentView.record_scroll_position()
self.contentView.record_position()
self.main_window.show()
self.contentView.setWindowFlags(QtCore.Qt.Widget)
@@ -419,26 +395,25 @@ class Tab(QtWidgets.QWidget):
if self.are_we_doing_images_only:
chapter = self.metadata['position']['current_chapter']
search_data = (0, None)
cursor_position = 0
else:
chapter, scroll_position, visible_text = self.contentView.record_scroll_position(True)
search_data = (scroll_position, visible_text)
chapter, cursor_position = self.contentView.record_position(True)
self.metadata['bookmarks'][identifier] = {
'chapter': chapter,
'search_data': search_data,
'cursor_position': cursor_position,
'description': description}
self.add_bookmark_to_model(
description, chapter, search_data, identifier)
description, chapter, cursor_position, identifier)
self.dockWidget.setVisible(True)
def add_bookmark_to_model(self, description, chapter, search_data, identifier):
def add_bookmark_to_model(self, description, chapter, cursor_position, identifier):
bookmark = QtGui.QStandardItem()
bookmark.setData(description, QtCore.Qt.DisplayRole)
bookmark.setData(chapter, QtCore.Qt.UserRole)
bookmark.setData(search_data, QtCore.Qt.UserRole + 1)
bookmark.setData(cursor_position, QtCore.Qt.UserRole + 1)
bookmark.setData(identifier, QtCore.Qt.UserRole + 2)
self.bookmark_model.appendRow(bookmark)
@@ -449,22 +424,30 @@ class Tab(QtWidgets.QWidget):
return
chapter = self.proxy_model.data(index, QtCore.Qt.UserRole)
search_data = self.proxy_model.data(index, QtCore.Qt.UserRole + 1)
cursor_position = self.proxy_model.data(index, QtCore.Qt.UserRole + 1)
self.main_window.bookToolBar.tocBox.setCurrentIndex(chapter - 1)
if not self.are_we_doing_images_only:
self.set_cursor_position(False, search_data)
self.set_cursor_position(cursor_position)
def generate_bookmark_model(self):
# TODO
# Sorting is not working correctly
for i in self.metadata['bookmarks'].items():
self.add_bookmark_to_model(
i[1]['description'],
i[1]['chapter'],
i[1]['search_data'],
i[0])
try:
for i in self.metadata['bookmarks'].items():
self.add_bookmark_to_model(
i[1]['description'],
i[1]['chapter'],
i[1]['cursor_position'],
i[0])
except KeyError:
title = self.metadata['title']
# TODO
# Delete the bookmarks entry for this file
print(f'Database: Bookmark error for {title}. Recommend delete entry.')
return
self.generate_bookmark_proxy_model()
@@ -579,7 +562,7 @@ class PliantQGraphicsView(QtWidgets.QGraphicsView):
image_pixmap.loadFromData(page_data)
elif self.filetype == 'pdf':
page_data = self.book.page(current_page)
page_qimage = page_data.renderToImage(350, 350)
page_qimage = page_data.renderToImage(400, 400) # TODO Maybe this needs a setting?
image_pixmap.convertFromImage(page_qimage)
return image_pixmap
@@ -672,7 +655,7 @@ class PliantQGraphicsView(QtWidgets.QGraphicsView):
self.show()
def wheelEvent(self, event):
self.common_functions.wheelEvent(event, True)
self.common_functions.wheelEvent(event)
def keyPressEvent(self, event):
vertical = self.verticalScrollBar().value()
@@ -712,6 +695,10 @@ class PliantQGraphicsView(QtWidgets.QGraphicsView):
if event.key() in view_modification_keys:
self.main_window.modify_comic_view(event.key())
def record_position(self):
self.parent.metadata['position']['is_read'] = False
self.common_functions.update_model()
def mouseMoveEvent(self, *args):
self.viewport().setCursor(QtCore.Qt.ArrowCursor)
self.parent.mouse_hide_timer.start(3000)
@@ -825,13 +812,13 @@ class PliantQTextBrowser(QtWidgets.QTextBrowser):
self.setMouseTracking(True)
self.viewport().setCursor(QtCore.Qt.IBeamCursor)
self.verticalScrollBar().sliderMoved.connect(
self.record_scroll_position)
self.record_position)
self.ignore_wheel_event = False
self.ignore_wheel_event_number = 0
def wheelEvent(self, event):
self.record_scroll_position()
self.common_functions.wheelEvent(event, False)
self.record_position()
self.common_functions.wheelEvent(event)
def keyPressEvent(self, event):
QtWidgets.QTextEdit.keyPressEvent(self, event)
@@ -840,6 +827,7 @@ class PliantQTextBrowser(QtWidgets.QTextBrowser):
self.common_functions.change_chapter(1, True)
else:
self.set_top_line_cleanly()
self.record_position()
def set_top_line_cleanly(self):
# Find the cursor position of the top line and move to it
@@ -849,22 +837,27 @@ class PliantQTextBrowser(QtWidgets.QTextBrowser):
self.setTextCursor(find_cursor)
self.ensureCursorVisible()
def record_scroll_position(self, return_as_bookmark=False):
def record_position(self, return_as_bookmark=False):
self.parent.metadata['position']['is_read'] = False
vertical = self.verticalScrollBar().value()
maximum = self.verticalScrollBar().maximum()
self.parent.metadata['position']['scroll_value'] = 1
if maximum != 0:
self.parent.metadata['position']['scroll_value'] = (vertical / maximum)
cursor = self.cursorForPosition(QtCore.QPoint(0, 0))
cursor_position = cursor.position()
# Current block for progress measurement
current_block = cursor.block().blockNumber()
current_chapter = self.parent.metadata['position']['current_chapter']
blocks_per_chapter = self.parent.metadata['position']['blocks_per_chapter']
block_sum = sum(blocks_per_chapter[:(current_chapter - 1)])
block_sum += current_block
# This 'current_block' refers to the number of
# blocks in the book upto this one
self.parent.metadata['position']['current_block'] = block_sum
self.common_functions.update_model()
if return_as_bookmark:
return (self.parent.metadata['position']['current_chapter'],
self.parent.metadata['position']['scroll_value'],
cursor_position)
else:
self.parent.metadata['position']['cursor_position'] = cursor_position
@@ -936,14 +929,15 @@ class PliantQTextBrowser(QtWidgets.QTextBrowser):
QtWidgets.QTextBrowser.mouseMoveEvent(self, event)
class PliantWidgetsCommonFunctions():
class PliantWidgetsCommonFunctions:
def __init__(self, parent_widget, main_window):
self.pw = parent_widget
self.main_window = main_window
self.are_we_doing_images_only = self.pw.parent.are_we_doing_images_only
def wheelEvent(self, event, are_we_doing_images_only):
def wheelEvent(self, event):
ignore_events = 20
if are_we_doing_images_only:
if self.are_we_doing_images_only:
ignore_events = 10
if self.pw.ignore_wheel_event:
@@ -953,7 +947,7 @@ class PliantWidgetsCommonFunctions():
self.pw.ignore_wheel_event_number = 0
return
if are_we_doing_images_only:
if self.are_we_doing_images_only:
QtWidgets.QGraphicsView.wheelEvent(self.pw, event)
else:
QtWidgets.QTextBrowser.wheelEvent(self.pw, event)
@@ -1002,6 +996,40 @@ class PliantWidgetsCommonFunctions():
if not was_button_pressed:
self.pw.ignore_wheel_event = True
if not self.are_we_doing_images_only:
self.pw.record_position()
def update_model(self):
# We're updating the underlying model to have real-time
# updates on the read status
# Set a baseline model index in case the item gets deleted
# E.g It's open in a tab and deleted from the library
model_index = None
start_index = self.main_window.lib_ref.view_model.index(0, 0)
# Find index of the model item that corresponds to the tab
model_index = self.main_window.lib_ref.view_model.match(
start_index,
QtCore.Qt.UserRole + 6,
self.pw.parent.metadata['hash'],
1, QtCore.Qt.MatchExactly)
if self.are_we_doing_images_only:
position_percentage = (self.pw.parent.metadata['position']['current_chapter'] /
self.pw.parent.metadata['position']['total_chapters'])
else:
position_percentage = (self.pw.parent.metadata['position']['current_block'] /
self.pw.parent.metadata['position']['total_blocks'])
# Update book metadata and position percentage
if model_index:
self.main_window.lib_ref.view_model.setData(
model_index[0], self.pw.parent.metadata, QtCore.Qt.UserRole + 3)
self.main_window.lib_ref.view_model.setData(
model_index[0], position_percentage, QtCore.Qt.UserRole + 7)
def generate_combo_box_action(self, contextMenu):
contextMenu.addSeparator()