Bookmark creation and navigation, Cleanup
This commit is contained in:
9
TODO
9
TODO
@@ -43,17 +43,17 @@ TODO
|
||||
✓ View and hide toolbar actions in a list
|
||||
✓ Line spacing
|
||||
✓ Record progress
|
||||
Bookmarks
|
||||
Set context menu for definitions and the like
|
||||
Search document using QTextCursor?
|
||||
Use embedded fonts
|
||||
Cache multiple images
|
||||
Graphical themes
|
||||
Comic view keyboard shortcuts
|
||||
Comic view modes
|
||||
Continuous paging
|
||||
Double pages
|
||||
Bookmarks
|
||||
Pagination
|
||||
Set context menu for definitions and the like
|
||||
Use embedded fonts
|
||||
Scrolling: Smooth / By Line
|
||||
Filetypes:
|
||||
✓ cbz, cbr support
|
||||
@@ -72,4 +72,5 @@ TODO
|
||||
✓ Include icons for emblems
|
||||
Shift to logging instead of print statements
|
||||
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)
|
||||
|
||||
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):
|
||||
if event:
|
||||
@@ -376,7 +378,6 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
# Remember file addition modality
|
||||
# If a file is added from here, it should not be removed
|
||||
# from the libary in case of a database refresh
|
||||
# Individually added files are not subject to library filtering
|
||||
|
||||
opened_files = QtWidgets.QFileDialog.getOpenFileNames(
|
||||
self, 'Open file', self.settings['last_open_path'],
|
||||
|
15
database.py
15
database.py
@@ -19,7 +19,6 @@
|
||||
import os
|
||||
import pickle
|
||||
import sqlite3
|
||||
|
||||
from PyQt5 import QtCore
|
||||
|
||||
|
||||
@@ -163,28 +162,30 @@ class DatabaseFunctions:
|
||||
return None
|
||||
|
||||
except (KeyError, sqlite3.OperationalError):
|
||||
print('SQLite is in rebellion, Commander')
|
||||
print('Commander, SQLite is in rebellion @ data fetching handling')
|
||||
|
||||
self.close_database()
|
||||
|
||||
def modify_position(self, hash_position_last_accessed):
|
||||
for i in hash_position_last_accessed:
|
||||
def modify_positional_data(self, positional_data):
|
||||
for i in positional_data:
|
||||
file_hash = i[0]
|
||||
position = i[1]
|
||||
last_accessed = i[2]
|
||||
bookmarks = i[3]
|
||||
|
||||
position_bin = sqlite3.Binary(pickle.dumps(position))
|
||||
last_accessed_bin = sqlite3.Binary(pickle.dumps(last_accessed))
|
||||
bookmarks_bin = sqlite3.Binary(pickle.dumps(bookmarks))
|
||||
|
||||
sql_command = (
|
||||
"UPDATE books SET Position = ?, LastAccessed = ? WHERE Hash = ?")
|
||||
"UPDATE books SET Position = ?, LastAccessed = ?, Bookmarks = ? WHERE Hash = ?")
|
||||
|
||||
try:
|
||||
self.database.execute(
|
||||
sql_command,
|
||||
[position_bin, last_accessed_bin, file_hash])
|
||||
[position_bin, last_accessed_bin, bookmarks_bin, file_hash])
|
||||
except sqlite3.OperationalError:
|
||||
print('SQLite is in rebellion, Commander')
|
||||
print('Commander, SQLite is in rebellion @ positional data handling')
|
||||
return
|
||||
|
||||
self.database.commit()
|
||||
|
@@ -27,12 +27,12 @@ class ItemProxyModel(QtCore.QSortFilterProxyModel):
|
||||
super(ItemProxyModel, self).__init__(parent)
|
||||
self.filter_text = 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.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):
|
||||
model = self.sourceModel()
|
||||
@@ -46,7 +46,8 @@ class ItemProxyModel(QtCore.QSortFilterProxyModel):
|
||||
directory_tags = model.data(this_index, QtCore.Qt.UserRole + 11)
|
||||
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
|
||||
|
||||
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):
|
||||
database_return = database.DatabaseFunctions(
|
||||
self.database_path).fetch_data(
|
||||
('DateAdded', 'Position', 'Bookmarks'),
|
||||
('Position', 'Bookmarks'),
|
||||
'books',
|
||||
{'Hash': file_hash},
|
||||
'EQUALS')[0]
|
||||
|
||||
book_data = []
|
||||
for i in database_return:
|
||||
# All of these values are pickled and stored
|
||||
if i:
|
||||
book_data.append(pickle.loads(i))
|
||||
else:
|
||||
@@ -214,12 +215,9 @@ class BookSorter:
|
||||
content['Invalid'] = 'Possible Parse Error'
|
||||
|
||||
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]['bookmarks'] = bookmarks
|
||||
this_book[file_md5]['content'] = content
|
||||
|
@@ -37,11 +37,13 @@ class BackGroundTabUpdate(QtCore.QThread):
|
||||
file_hash = i['hash']
|
||||
position = i['position']
|
||||
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(
|
||||
self.database_path).modify_position(hash_position_pairs)
|
||||
self.database_path).modify_positional_data(hash_position_pairs)
|
||||
|
||||
|
||||
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()
|
||||
|
||||
position = self.metadata['position']
|
||||
|
||||
if position:
|
||||
current_chapter = position['current_chapter']
|
||||
else:
|
||||
@@ -420,10 +419,10 @@ class Tab(QtWidgets.QWidget):
|
||||
self.contentView.setHtml(chapter_content)
|
||||
self.contentView.setReadOnly(True)
|
||||
|
||||
temp_hidden_button = QtWidgets.QToolButton(self)
|
||||
temp_hidden_button.setVisible(False)
|
||||
temp_hidden_button.clicked.connect(self.set_scroll_value)
|
||||
temp_hidden_button.animateClick(100)
|
||||
tempHiddenButton = QtWidgets.QToolButton(self)
|
||||
tempHiddenButton.setVisible(False)
|
||||
tempHiddenButton.clicked.connect(self.set_scroll_value)
|
||||
tempHiddenButton.animateClick(100)
|
||||
|
||||
# The following are common to both the text browser and
|
||||
# the graphics view
|
||||
@@ -437,12 +436,18 @@ class Tab(QtWidgets.QWidget):
|
||||
self.dockWidget = QtWidgets.QDockWidget(self)
|
||||
self.dockWidget.setFeatures(QtWidgets.QDockWidget.DockWidgetClosable)
|
||||
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.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.horzLayout.addWidget(self.contentView)
|
||||
@@ -470,21 +475,25 @@ class Tab(QtWidgets.QWidget):
|
||||
self.window().lib_ref.view_model.setData(
|
||||
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:
|
||||
previous_widget = self.window().tabWidget.currentWidget()
|
||||
self.window().tabWidget.setCurrentWidget(self)
|
||||
|
||||
scroll_position = (
|
||||
self.metadata['position']['scroll_value'] *
|
||||
self.contentView.verticalScrollBar().maximum())
|
||||
scroll_value = self.metadata['position']['scroll_value']
|
||||
if search_data:
|
||||
scroll_value = search_data[0]
|
||||
|
||||
# Scroll a little ahead
|
||||
# This avoids confusion with potentially duplicate phrases
|
||||
# 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']
|
||||
if search_data:
|
||||
last_visible_text = search_data[1]
|
||||
|
||||
if last_visible_text:
|
||||
self.contentView.find(last_visible_text)
|
||||
|
||||
@@ -501,7 +510,6 @@ class Tab(QtWidgets.QWidget):
|
||||
# Calculate lines to incorporate into progress
|
||||
self.metadata['position'] = {
|
||||
'current_chapter': 1,
|
||||
'current_line': 0,
|
||||
'total_chapters': total_chapters,
|
||||
'scroll_value': 0,
|
||||
'last_visible_text': None}
|
||||
@@ -579,6 +587,10 @@ class Tab(QtWidgets.QWidget):
|
||||
block_format.setLineHeight(
|
||||
line_spacing, QtGui.QTextBlockFormat.ProportionalHeight)
|
||||
|
||||
# TODO
|
||||
# Give options for alignment
|
||||
# block_format.setAlignment(QtCore.Qt.AlignJustify)
|
||||
|
||||
# Also for padding
|
||||
# Using setViewPortMargins for this disables scrolling in the margins
|
||||
block_format.setLeftMargin(padding)
|
||||
@@ -605,6 +617,47 @@ class Tab(QtWidgets.QWidget):
|
||||
else:
|
||||
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):
|
||||
self.contentView.setCursor(QtCore.Qt.BlankCursor)
|
||||
|
||||
@@ -629,11 +682,18 @@ class PliantQGraphicsView(QtWidgets.QGraphicsView):
|
||||
self.image_pixmap = None
|
||||
self.ignore_wheel_event = False
|
||||
self.ignore_wheel_event_number = 0
|
||||
self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
|
||||
self.common_functions = PliantWidgetsCommonFunctions(
|
||||
self, self.main_window)
|
||||
self.setMouseTracking(True)
|
||||
|
||||
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.load(image_path)
|
||||
self.resizeEvent()
|
||||
@@ -731,7 +791,7 @@ class PliantQTextBrowser(QtWidgets.QTextBrowser):
|
||||
else:
|
||||
QtWidgets.QTextEdit.keyPressEvent(self, event)
|
||||
|
||||
def record_scroll_position(self):
|
||||
def record_scroll_position(self, return_as_bookmark=False):
|
||||
vertical = self.verticalScrollBar().value()
|
||||
maximum = self.verticalScrollBar().maximum()
|
||||
|
||||
@@ -747,7 +807,13 @@ class PliantQTextBrowser(QtWidgets.QTextBrowser):
|
||||
|
||||
if len(visible_text) > 50:
|
||||
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):
|
||||
# TODO
|
||||
|
Reference in New Issue
Block a user