Bookmark creation and navigation, Cleanup
This commit is contained in:
7
TODO
7
TODO
@@ -43,17 +43,17 @@ TODO
|
|||||||
✓ View and hide toolbar actions in a list
|
✓ View and hide toolbar actions in a list
|
||||||
✓ Line spacing
|
✓ Line spacing
|
||||||
✓ Record progress
|
✓ Record progress
|
||||||
|
Bookmarks
|
||||||
|
Set context menu for definitions and the like
|
||||||
Search document using QTextCursor?
|
Search document using QTextCursor?
|
||||||
Use embedded fonts
|
|
||||||
Cache multiple images
|
Cache multiple images
|
||||||
Graphical themes
|
Graphical themes
|
||||||
Comic view keyboard shortcuts
|
Comic view keyboard shortcuts
|
||||||
Comic view modes
|
Comic view modes
|
||||||
Continuous paging
|
Continuous paging
|
||||||
Double pages
|
Double pages
|
||||||
Bookmarks
|
|
||||||
Pagination
|
Pagination
|
||||||
Set context menu for definitions and the like
|
Use embedded fonts
|
||||||
Scrolling: Smooth / By Line
|
Scrolling: Smooth / By Line
|
||||||
Filetypes:
|
Filetypes:
|
||||||
✓ cbz, cbr support
|
✓ cbz, cbr support
|
||||||
@@ -73,3 +73,4 @@ TODO
|
|||||||
Shift to logging instead of print statements
|
Shift to logging instead of print statements
|
||||||
Bugs:
|
Bugs:
|
||||||
If there are files open and the database is deleted, TypeErrors result
|
If there are files open and the database is deleted, TypeErrors result
|
||||||
|
Closing a fullscreened contentView does not save settings
|
@@ -334,7 +334,9 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
|||||||
item.setData(True, QtCore.Qt.UserRole + 8)
|
item.setData(True, QtCore.Qt.UserRole + 8)
|
||||||
|
|
||||||
def test_function(self):
|
def test_function(self):
|
||||||
print('Caesar si viveret, ad remum dareris')
|
# print('Caesar si viveret, ad remum dareris')
|
||||||
|
if self.tabWidget.currentIndex() != 0:
|
||||||
|
self.tabWidget.widget(self.tabWidget.currentIndex()).add_bookmark()
|
||||||
|
|
||||||
def resizeEvent(self, event=None):
|
def resizeEvent(self, event=None):
|
||||||
if event:
|
if event:
|
||||||
@@ -376,7 +378,6 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
|||||||
# Remember file addition modality
|
# Remember file addition modality
|
||||||
# If a file is added from here, it should not be removed
|
# If a file is added from here, it should not be removed
|
||||||
# from the libary in case of a database refresh
|
# from the libary in case of a database refresh
|
||||||
# Individually added files are not subject to library filtering
|
|
||||||
|
|
||||||
opened_files = QtWidgets.QFileDialog.getOpenFileNames(
|
opened_files = QtWidgets.QFileDialog.getOpenFileNames(
|
||||||
self, 'Open file', self.settings['last_open_path'],
|
self, 'Open file', self.settings['last_open_path'],
|
||||||
|
15
database.py
15
database.py
@@ -19,7 +19,6 @@
|
|||||||
import os
|
import os
|
||||||
import pickle
|
import pickle
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
from PyQt5 import QtCore
|
from PyQt5 import QtCore
|
||||||
|
|
||||||
|
|
||||||
@@ -163,28 +162,30 @@ class DatabaseFunctions:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
except (KeyError, sqlite3.OperationalError):
|
except (KeyError, sqlite3.OperationalError):
|
||||||
print('SQLite is in rebellion, Commander')
|
print('Commander, SQLite is in rebellion @ data fetching handling')
|
||||||
|
|
||||||
self.close_database()
|
self.close_database()
|
||||||
|
|
||||||
def modify_position(self, hash_position_last_accessed):
|
def modify_positional_data(self, positional_data):
|
||||||
for i in hash_position_last_accessed:
|
for i in positional_data:
|
||||||
file_hash = i[0]
|
file_hash = i[0]
|
||||||
position = i[1]
|
position = i[1]
|
||||||
last_accessed = i[2]
|
last_accessed = i[2]
|
||||||
|
bookmarks = i[3]
|
||||||
|
|
||||||
position_bin = sqlite3.Binary(pickle.dumps(position))
|
position_bin = sqlite3.Binary(pickle.dumps(position))
|
||||||
last_accessed_bin = sqlite3.Binary(pickle.dumps(last_accessed))
|
last_accessed_bin = sqlite3.Binary(pickle.dumps(last_accessed))
|
||||||
|
bookmarks_bin = sqlite3.Binary(pickle.dumps(bookmarks))
|
||||||
|
|
||||||
sql_command = (
|
sql_command = (
|
||||||
"UPDATE books SET Position = ?, LastAccessed = ? WHERE Hash = ?")
|
"UPDATE books SET Position = ?, LastAccessed = ?, Bookmarks = ? WHERE Hash = ?")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.database.execute(
|
self.database.execute(
|
||||||
sql_command,
|
sql_command,
|
||||||
[position_bin, last_accessed_bin, file_hash])
|
[position_bin, last_accessed_bin, bookmarks_bin, file_hash])
|
||||||
except sqlite3.OperationalError:
|
except sqlite3.OperationalError:
|
||||||
print('SQLite is in rebellion, Commander')
|
print('Commander, SQLite is in rebellion @ positional data handling')
|
||||||
return
|
return
|
||||||
|
|
||||||
self.database.commit()
|
self.database.commit()
|
||||||
|
@@ -27,12 +27,12 @@ class ItemProxyModel(QtCore.QSortFilterProxyModel):
|
|||||||
super(ItemProxyModel, self).__init__(parent)
|
super(ItemProxyModel, self).__init__(parent)
|
||||||
self.filter_text = None
|
self.filter_text = None
|
||||||
self.active_library_filters = None
|
self.active_library_filters = None
|
||||||
self.sorting_position = None
|
self.sorting_box_position = None
|
||||||
|
|
||||||
def setFilterParams(self, filter_text, active_library_filters, sorting_position):
|
def setFilterParams(self, filter_text, active_library_filters, sorting_box_position):
|
||||||
self.filter_text = filter_text
|
self.filter_text = filter_text
|
||||||
self.active_library_filters = [i.lower() for i in active_library_filters]
|
self.active_library_filters = [i.lower() for i in active_library_filters]
|
||||||
self.sorting_position = sorting_position
|
self.sorting_box_position = sorting_box_position
|
||||||
|
|
||||||
def filterAcceptsRow(self, row, parent):
|
def filterAcceptsRow(self, row, parent):
|
||||||
model = self.sourceModel()
|
model = self.sourceModel()
|
||||||
@@ -46,7 +46,8 @@ class ItemProxyModel(QtCore.QSortFilterProxyModel):
|
|||||||
directory_tags = model.data(this_index, QtCore.Qt.UserRole + 11)
|
directory_tags = model.data(this_index, QtCore.Qt.UserRole + 11)
|
||||||
last_accessed = model.data(this_index, QtCore.Qt.UserRole + 12)
|
last_accessed = model.data(this_index, QtCore.Qt.UserRole + 12)
|
||||||
|
|
||||||
if self.sorting_position == 4 and not last_accessed:
|
# Hide untouched files when sorting by last accessed
|
||||||
|
if self.sorting_box_position == 4 and not last_accessed:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self.active_library_filters:
|
if self.active_library_filters:
|
||||||
|
10
sorter.py
10
sorter.py
@@ -110,13 +110,14 @@ class BookSorter:
|
|||||||
def database_entry_for_book(self, file_hash):
|
def database_entry_for_book(self, file_hash):
|
||||||
database_return = database.DatabaseFunctions(
|
database_return = database.DatabaseFunctions(
|
||||||
self.database_path).fetch_data(
|
self.database_path).fetch_data(
|
||||||
('DateAdded', 'Position', 'Bookmarks'),
|
('Position', 'Bookmarks'),
|
||||||
'books',
|
'books',
|
||||||
{'Hash': file_hash},
|
{'Hash': file_hash},
|
||||||
'EQUALS')[0]
|
'EQUALS')[0]
|
||||||
|
|
||||||
book_data = []
|
book_data = []
|
||||||
for i in database_return:
|
for i in database_return:
|
||||||
|
# All of these values are pickled and stored
|
||||||
if i:
|
if i:
|
||||||
book_data.append(pickle.loads(i))
|
book_data.append(pickle.loads(i))
|
||||||
else:
|
else:
|
||||||
@@ -214,12 +215,9 @@ class BookSorter:
|
|||||||
content['Invalid'] = 'Possible Parse Error'
|
content['Invalid'] = 'Possible Parse Error'
|
||||||
|
|
||||||
book_data = self.database_entry_for_book(file_md5)
|
book_data = self.database_entry_for_book(file_md5)
|
||||||
|
position = book_data[0]
|
||||||
|
bookmarks = book_data[1]
|
||||||
|
|
||||||
date_added = book_data[0]
|
|
||||||
position = book_data[1]
|
|
||||||
bookmarks = book_data[2]
|
|
||||||
|
|
||||||
this_book[file_md5]['date_added'] = date_added
|
|
||||||
this_book[file_md5]['position'] = position
|
this_book[file_md5]['position'] = position
|
||||||
this_book[file_md5]['bookmarks'] = bookmarks
|
this_book[file_md5]['bookmarks'] = bookmarks
|
||||||
this_book[file_md5]['content'] = content
|
this_book[file_md5]['content'] = content
|
||||||
|
@@ -37,11 +37,13 @@ class BackGroundTabUpdate(QtCore.QThread):
|
|||||||
file_hash = i['hash']
|
file_hash = i['hash']
|
||||||
position = i['position']
|
position = i['position']
|
||||||
last_accessed = i['last_accessed']
|
last_accessed = i['last_accessed']
|
||||||
|
bookmarks = i['bookmarks']
|
||||||
|
|
||||||
hash_position_pairs.append([file_hash, position, last_accessed])
|
hash_position_pairs.append(
|
||||||
|
[file_hash, position, last_accessed, bookmarks])
|
||||||
|
|
||||||
database.DatabaseFunctions(
|
database.DatabaseFunctions(
|
||||||
self.database_path).modify_position(hash_position_pairs)
|
self.database_path).modify_positional_data(hash_position_pairs)
|
||||||
|
|
||||||
|
|
||||||
class BackGroundBookAddition(QtCore.QThread):
|
class BackGroundBookAddition(QtCore.QThread):
|
||||||
|
100
widgets.py
100
widgets.py
@@ -386,7 +386,6 @@ class Tab(QtWidgets.QWidget):
|
|||||||
self.metadata['last_accessed'] = QtCore.QDateTime().currentDateTime()
|
self.metadata['last_accessed'] = QtCore.QDateTime().currentDateTime()
|
||||||
|
|
||||||
position = self.metadata['position']
|
position = self.metadata['position']
|
||||||
|
|
||||||
if position:
|
if position:
|
||||||
current_chapter = position['current_chapter']
|
current_chapter = position['current_chapter']
|
||||||
else:
|
else:
|
||||||
@@ -420,10 +419,10 @@ class Tab(QtWidgets.QWidget):
|
|||||||
self.contentView.setHtml(chapter_content)
|
self.contentView.setHtml(chapter_content)
|
||||||
self.contentView.setReadOnly(True)
|
self.contentView.setReadOnly(True)
|
||||||
|
|
||||||
temp_hidden_button = QtWidgets.QToolButton(self)
|
tempHiddenButton = QtWidgets.QToolButton(self)
|
||||||
temp_hidden_button.setVisible(False)
|
tempHiddenButton.setVisible(False)
|
||||||
temp_hidden_button.clicked.connect(self.set_scroll_value)
|
tempHiddenButton.clicked.connect(self.set_scroll_value)
|
||||||
temp_hidden_button.animateClick(100)
|
tempHiddenButton.animateClick(100)
|
||||||
|
|
||||||
# The following are common to both the text browser and
|
# The following are common to both the text browser and
|
||||||
# the graphics view
|
# the graphics view
|
||||||
@@ -437,12 +436,18 @@ class Tab(QtWidgets.QWidget):
|
|||||||
self.dockWidget = QtWidgets.QDockWidget(self)
|
self.dockWidget = QtWidgets.QDockWidget(self)
|
||||||
self.dockWidget.setFeatures(QtWidgets.QDockWidget.DockWidgetClosable)
|
self.dockWidget.setFeatures(QtWidgets.QDockWidget.DockWidgetClosable)
|
||||||
self.dockWidget.setFloating(False)
|
self.dockWidget.setFloating(False)
|
||||||
self.dockListWidget = QtWidgets.QListWidget()
|
|
||||||
self.dockListWidget.setResizeMode(QtWidgets.QListWidget.Adjust)
|
|
||||||
self.dockListWidget.setMaximumWidth(350)
|
|
||||||
self.dockWidget.setWidget(self.dockListWidget)
|
|
||||||
self.dockWidget.hide()
|
self.dockWidget.hide()
|
||||||
|
|
||||||
|
self.dockListView = QtWidgets.QListView(self.dockWidget)
|
||||||
|
self.dockListView.setResizeMode(QtWidgets.QListWidget.Adjust)
|
||||||
|
self.dockListView.setMaximumWidth(350)
|
||||||
|
self.dockListView.clicked.connect(self.navigate_to_bookmark)
|
||||||
|
self.dockWidget.setWidget(self.dockListView)
|
||||||
|
|
||||||
|
self.bookmark_model = QtGui.QStandardItemModel()
|
||||||
|
self.generate_bookmark_model()
|
||||||
|
self.dockListView.setModel(self.bookmark_model)
|
||||||
|
|
||||||
self.generate_keyboard_shortcuts()
|
self.generate_keyboard_shortcuts()
|
||||||
|
|
||||||
self.horzLayout.addWidget(self.contentView)
|
self.horzLayout.addWidget(self.contentView)
|
||||||
@@ -470,21 +475,25 @@ class Tab(QtWidgets.QWidget):
|
|||||||
self.window().lib_ref.view_model.setData(
|
self.window().lib_ref.view_model.setData(
|
||||||
matching_item[0], self.metadata['last_accessed'], QtCore.Qt.UserRole + 12)
|
matching_item[0], self.metadata['last_accessed'], QtCore.Qt.UserRole + 12)
|
||||||
|
|
||||||
def set_scroll_value(self, switch_widgets=True):
|
def set_scroll_value(self, switch_widgets=True, search_data=None):
|
||||||
if switch_widgets:
|
if switch_widgets:
|
||||||
previous_widget = self.window().tabWidget.currentWidget()
|
previous_widget = self.window().tabWidget.currentWidget()
|
||||||
self.window().tabWidget.setCurrentWidget(self)
|
self.window().tabWidget.setCurrentWidget(self)
|
||||||
|
|
||||||
scroll_position = (
|
scroll_value = self.metadata['position']['scroll_value']
|
||||||
self.metadata['position']['scroll_value'] *
|
if search_data:
|
||||||
self.contentView.verticalScrollBar().maximum())
|
scroll_value = search_data[0]
|
||||||
|
|
||||||
# Scroll a little ahead
|
# Scroll a little ahead
|
||||||
# This avoids confusion with potentially duplicate phrases
|
# This avoids confusion with potentially duplicate phrases
|
||||||
# And the found result is at the top of the window
|
# And the found result is at the top of the window
|
||||||
self.contentView.verticalScrollBar().setValue(scroll_position * 1.1)
|
scroll_position = scroll_value * self.contentView.verticalScrollBar().maximum() * 1.1
|
||||||
|
self.contentView.verticalScrollBar().setValue(scroll_position)
|
||||||
|
|
||||||
last_visible_text = self.metadata['position']['last_visible_text']
|
last_visible_text = self.metadata['position']['last_visible_text']
|
||||||
|
if search_data:
|
||||||
|
last_visible_text = search_data[1]
|
||||||
|
|
||||||
if last_visible_text:
|
if last_visible_text:
|
||||||
self.contentView.find(last_visible_text)
|
self.contentView.find(last_visible_text)
|
||||||
|
|
||||||
@@ -501,7 +510,6 @@ class Tab(QtWidgets.QWidget):
|
|||||||
# Calculate lines to incorporate into progress
|
# Calculate lines to incorporate into progress
|
||||||
self.metadata['position'] = {
|
self.metadata['position'] = {
|
||||||
'current_chapter': 1,
|
'current_chapter': 1,
|
||||||
'current_line': 0,
|
|
||||||
'total_chapters': total_chapters,
|
'total_chapters': total_chapters,
|
||||||
'scroll_value': 0,
|
'scroll_value': 0,
|
||||||
'last_visible_text': None}
|
'last_visible_text': None}
|
||||||
@@ -579,6 +587,10 @@ class Tab(QtWidgets.QWidget):
|
|||||||
block_format.setLineHeight(
|
block_format.setLineHeight(
|
||||||
line_spacing, QtGui.QTextBlockFormat.ProportionalHeight)
|
line_spacing, QtGui.QTextBlockFormat.ProportionalHeight)
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
# Give options for alignment
|
||||||
|
# block_format.setAlignment(QtCore.Qt.AlignJustify)
|
||||||
|
|
||||||
# Also for padding
|
# Also for padding
|
||||||
# Using setViewPortMargins for this disables scrolling in the margins
|
# Using setViewPortMargins for this disables scrolling in the margins
|
||||||
block_format.setLeftMargin(padding)
|
block_format.setLeftMargin(padding)
|
||||||
@@ -605,6 +617,47 @@ class Tab(QtWidgets.QWidget):
|
|||||||
else:
|
else:
|
||||||
self.dockWidget.show()
|
self.dockWidget.show()
|
||||||
|
|
||||||
|
def add_bookmark(self):
|
||||||
|
chapter, scroll_position, visible_text = self.contentView.record_scroll_position(True)
|
||||||
|
description = 'New bookmark'
|
||||||
|
search_data = (scroll_position, visible_text)
|
||||||
|
|
||||||
|
self.metadata['bookmarks'].append([
|
||||||
|
chapter, search_data, description])
|
||||||
|
self.add_bookmark_to_model(description, chapter, search_data)
|
||||||
|
|
||||||
|
def generate_bookmark_model(self):
|
||||||
|
bookmarks = self.metadata['bookmarks']
|
||||||
|
|
||||||
|
if not bookmarks:
|
||||||
|
self.metadata['bookmarks'] = []
|
||||||
|
return
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
# Replace this with proxy model sorting
|
||||||
|
bookmarks.sort(key=lambda x: x[0])
|
||||||
|
|
||||||
|
for i in bookmarks:
|
||||||
|
self.add_bookmark_to_model(i[2], i[0], i[1])
|
||||||
|
|
||||||
|
def add_bookmark_to_model(self, description, chapter, search_data):
|
||||||
|
bookmark = QtGui.QStandardItem()
|
||||||
|
bookmark.setData(description, QtCore.Qt.DisplayRole)
|
||||||
|
bookmark.setData(chapter, QtCore.Qt.UserRole)
|
||||||
|
bookmark.setData(search_data, QtCore.Qt.UserRole + 1)
|
||||||
|
|
||||||
|
self.bookmark_model.appendRow(bookmark)
|
||||||
|
|
||||||
|
def navigate_to_bookmark(self, index):
|
||||||
|
if not index.isValid():
|
||||||
|
return
|
||||||
|
|
||||||
|
chapter = self.bookmark_model.data(index, QtCore.Qt.UserRole)
|
||||||
|
search_data = self.bookmark_model.data(index, QtCore.Qt.UserRole + 1)
|
||||||
|
|
||||||
|
self.window().bookToolBar.tocBox.setCurrentIndex(chapter - 1)
|
||||||
|
self.set_scroll_value(False, search_data)
|
||||||
|
|
||||||
def hide_mouse(self):
|
def hide_mouse(self):
|
||||||
self.contentView.setCursor(QtCore.Qt.BlankCursor)
|
self.contentView.setCursor(QtCore.Qt.BlankCursor)
|
||||||
|
|
||||||
@@ -629,11 +682,18 @@ class PliantQGraphicsView(QtWidgets.QGraphicsView):
|
|||||||
self.image_pixmap = None
|
self.image_pixmap = None
|
||||||
self.ignore_wheel_event = False
|
self.ignore_wheel_event = False
|
||||||
self.ignore_wheel_event_number = 0
|
self.ignore_wheel_event_number = 0
|
||||||
|
self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
|
||||||
self.common_functions = PliantWidgetsCommonFunctions(
|
self.common_functions = PliantWidgetsCommonFunctions(
|
||||||
self, self.main_window)
|
self, self.main_window)
|
||||||
self.setMouseTracking(True)
|
self.setMouseTracking(True)
|
||||||
|
|
||||||
def loadImage(self, image_path):
|
def loadImage(self, image_path):
|
||||||
|
# TODO
|
||||||
|
# Cache 4 images
|
||||||
|
# For single page view: 1 before, 2 after
|
||||||
|
# For double page view: 1 before, 1 after
|
||||||
|
# Image panning with mouse
|
||||||
|
|
||||||
self.image_pixmap = QtGui.QPixmap()
|
self.image_pixmap = QtGui.QPixmap()
|
||||||
self.image_pixmap.load(image_path)
|
self.image_pixmap.load(image_path)
|
||||||
self.resizeEvent()
|
self.resizeEvent()
|
||||||
@@ -731,7 +791,7 @@ class PliantQTextBrowser(QtWidgets.QTextBrowser):
|
|||||||
else:
|
else:
|
||||||
QtWidgets.QTextEdit.keyPressEvent(self, event)
|
QtWidgets.QTextEdit.keyPressEvent(self, event)
|
||||||
|
|
||||||
def record_scroll_position(self):
|
def record_scroll_position(self, return_as_bookmark=False):
|
||||||
vertical = self.verticalScrollBar().value()
|
vertical = self.verticalScrollBar().value()
|
||||||
maximum = self.verticalScrollBar().maximum()
|
maximum = self.verticalScrollBar().maximum()
|
||||||
|
|
||||||
@@ -747,7 +807,13 @@ class PliantQTextBrowser(QtWidgets.QTextBrowser):
|
|||||||
|
|
||||||
if len(visible_text) > 50:
|
if len(visible_text) > 50:
|
||||||
visible_text = visible_text[:51]
|
visible_text = visible_text[:51]
|
||||||
self.parent.metadata['position']['last_visible_text'] = visible_text
|
|
||||||
|
if return_as_bookmark:
|
||||||
|
return (self.parent.metadata['position']['current_chapter'],
|
||||||
|
self.parent.metadata['position']['scroll_value'],
|
||||||
|
visible_text)
|
||||||
|
else:
|
||||||
|
self.parent.metadata['position']['last_visible_text'] = visible_text
|
||||||
|
|
||||||
# def mouseMoveEvent(self, event):
|
# def mouseMoveEvent(self, event):
|
||||||
# TODO
|
# TODO
|
||||||
|
Reference in New Issue
Block a user