Multiple fixes
Images are now center aligned Better logging
This commit is contained in:
@@ -302,9 +302,10 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
for count, i in enumerate(self.settings['main_window_headers']):
|
||||
self.tableView.horizontalHeader().resizeSection(count, int(i))
|
||||
self.tableView.horizontalHeader().resizeSection(5, 30)
|
||||
self.tableView.horizontalHeader().setStretchLastSection(False)
|
||||
self.tableView.horizontalHeader().setStretchLastSection(True)
|
||||
self.tableView.horizontalHeader().sectionClicked.connect(
|
||||
self.lib_ref.tableProxyModel.sort_table_columns)
|
||||
self.lib_ref.tableProxyModel.sort_table_columns(2)
|
||||
self.tableView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||
self.tableView.customContextMenuRequested.connect(
|
||||
self.generate_library_context_menu)
|
||||
@@ -360,7 +361,6 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
def process_post_hoc_files(self, file_list, open_files_after_processing):
|
||||
# Takes care of both dragged and dropped files
|
||||
# As well as files sent as command line arguments
|
||||
|
||||
file_list = [i for i in file_list if os.path.exists(i)]
|
||||
if not file_list:
|
||||
return
|
||||
@@ -390,7 +390,6 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
# This allows for threading file opening
|
||||
# Which should speed up multiple file opening
|
||||
# especially @ application start
|
||||
|
||||
file_paths = [i for i in path_hash_dictionary]
|
||||
|
||||
for filename in path_hash_dictionary.items():
|
||||
@@ -520,15 +519,15 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
self.thread.finished.connect(self.move_on)
|
||||
self.thread.start()
|
||||
|
||||
def get_selection(self, library_widget):
|
||||
def get_selection(self):
|
||||
selected_indexes = None
|
||||
|
||||
if library_widget == self.listView:
|
||||
if self.listView.isVisible():
|
||||
selected_books = self.lib_ref.itemProxyModel.mapSelectionToSource(
|
||||
self.listView.selectionModel().selection())
|
||||
selected_indexes = [i.indexes()[0] for i in selected_books]
|
||||
|
||||
elif library_widget == self.tableView:
|
||||
elif self.tableView.isVisible():
|
||||
selected_books = self.tableView.selectionModel().selectedRows()
|
||||
selected_indexes = [
|
||||
self.lib_ref.tableProxyModel.mapToSource(i) for i in selected_books]
|
||||
@@ -536,16 +535,10 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
return selected_indexes
|
||||
|
||||
def delete_books(self, selected_indexes=None):
|
||||
if not selected_indexes:
|
||||
# Get a list of QItemSelection objects
|
||||
# What we're interested in is the indexes()[0] in each of them
|
||||
# That gives a list of indexes from the view model
|
||||
if self.listView.isVisible():
|
||||
selected_indexes = self.get_selection(self.listView)
|
||||
|
||||
elif self.tableView.isVisible():
|
||||
selected_indexes = self.get_selection(self.tableView)
|
||||
|
||||
# Get a list of QItemSelection objects
|
||||
# What we're interested in is the indexes()[0] in each of them
|
||||
# That gives a list of indexes from the view model
|
||||
selected_indexes = self.get_selection()
|
||||
if not selected_indexes:
|
||||
return
|
||||
|
||||
@@ -572,10 +565,9 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
self.thread.start()
|
||||
|
||||
# Generate a message box to confirm deletion
|
||||
selected_number = len(selected_indexes)
|
||||
confirm_deletion = QtWidgets.QMessageBox()
|
||||
deletion_prompt = self._translate(
|
||||
'Main_UI', f'Delete {selected_number} book(s)?')
|
||||
'Main_UI', f'Delete book(s)?')
|
||||
confirm_deletion.setText(deletion_prompt)
|
||||
confirm_deletion.setIcon(QtWidgets.QMessageBox.Question)
|
||||
confirm_deletion.setWindowTitle(self._translate('Main_UI', 'Confirm deletion'))
|
||||
@@ -770,6 +762,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
signal_sender = None
|
||||
else:
|
||||
signal_sender = self.sender().objectName()
|
||||
|
||||
self.profile_functions.modify_comic_view(
|
||||
signal_sender, key_pressed)
|
||||
|
||||
@@ -805,7 +798,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
|
||||
# It's worth remembering that these are indexes of the libraryModel
|
||||
# and NOT of the proxy models
|
||||
selected_indexes = self.get_selection(self.sender())
|
||||
selected_indexes = self.get_selection()
|
||||
|
||||
context_menu = QtWidgets.QMenu()
|
||||
|
||||
|
@@ -241,6 +241,7 @@ class ViewProfileModification:
|
||||
self.format_contentView()
|
||||
|
||||
def modify_comic_view(self, signal_sender, key_pressed):
|
||||
comic_profile = self.main_window.comic_profile
|
||||
current_tab = self.tabWidget.widget(self.tabWidget.currentIndex())
|
||||
|
||||
self.bookToolBar.fitWidth.setChecked(False)
|
||||
@@ -248,34 +249,35 @@ class ViewProfileModification:
|
||||
self.bookToolBar.originalSize.setChecked(False)
|
||||
|
||||
if signal_sender == 'zoomOut' or key_pressed == QtCore.Qt.Key_Minus:
|
||||
self.comic_profile['zoom_mode'] = 'manualZoom'
|
||||
self.comic_profile['padding'] += 50
|
||||
comic_profile['zoom_mode'] = 'manualZoom'
|
||||
comic_profile['padding'] += 50
|
||||
|
||||
# This prevents infinite zoom out
|
||||
if self.comic_profile['padding'] * 2 > current_tab.contentView.viewport().width():
|
||||
self.comic_profile['padding'] -= 50
|
||||
if comic_profile['padding'] * 2 > current_tab.contentView.viewport().width():
|
||||
comic_profile['padding'] -= 50
|
||||
|
||||
if signal_sender == 'zoomIn' or key_pressed in (QtCore.Qt.Key_Plus, QtCore.Qt.Key_Equal):
|
||||
self.comic_profile['zoom_mode'] = 'manualZoom'
|
||||
self.comic_profile['padding'] -= 50
|
||||
if signal_sender == 'zoomIn' or key_pressed in (
|
||||
QtCore.Qt.Key_Plus, QtCore.Qt.Key_Equal):
|
||||
comic_profile['zoom_mode'] = 'manualZoom'
|
||||
comic_profile['padding'] -= 50
|
||||
|
||||
# This prevents infinite zoom in
|
||||
if self.comic_profile['padding'] < 0:
|
||||
self.comic_profile['padding'] = 0
|
||||
if comic_profile['padding'] < 0:
|
||||
comic_profile['padding'] = 0
|
||||
|
||||
if signal_sender == 'fitWidth' or key_pressed == QtCore.Qt.Key_W:
|
||||
self.comic_profile['zoom_mode'] = 'fitWidth'
|
||||
self.comic_profile['padding'] = 0
|
||||
comic_profile['zoom_mode'] = 'fitWidth'
|
||||
comic_profile['padding'] = 0
|
||||
self.bookToolBar.fitWidth.setChecked(True)
|
||||
|
||||
# Padding in the following cases is decided by
|
||||
# the image pixmap loaded by the widget
|
||||
if signal_sender == 'bestFit' or key_pressed == QtCore.Qt.Key_B:
|
||||
self.comic_profile['zoom_mode'] = 'bestFit'
|
||||
comic_profile['zoom_mode'] = 'bestFit'
|
||||
self.bookToolBar.bestFit.setChecked(True)
|
||||
|
||||
if signal_sender == 'originalSize' or key_pressed == QtCore.Qt.Key_O:
|
||||
self.comic_profile['zoom_mode'] = 'originalSize'
|
||||
comic_profile['zoom_mode'] = 'originalSize'
|
||||
self.bookToolBar.originalSize.setChecked(True)
|
||||
|
||||
self.format_contentView()
|
||||
@@ -290,7 +292,6 @@ class ViewProfileModification:
|
||||
|
||||
if current_metadata['images_only']:
|
||||
background = self.comic_profile['background']
|
||||
padding = self.comic_profile['padding']
|
||||
zoom_mode = self.comic_profile['zoom_mode']
|
||||
|
||||
if zoom_mode == 'fitWidth':
|
||||
@@ -304,7 +305,7 @@ class ViewProfileModification:
|
||||
'background-color: %s' % background.name())
|
||||
|
||||
current_tab.format_view(
|
||||
None, None, None, background, padding, None, None)
|
||||
None, None, None, background, None, None, None)
|
||||
|
||||
else:
|
||||
profile_index = self.bookToolBar.profileBox.currentIndex()
|
||||
|
@@ -27,11 +27,7 @@ class ParsePDF:
|
||||
self.book = None
|
||||
|
||||
def read_book(self):
|
||||
try:
|
||||
self.book = fitz.open(self.filename)
|
||||
return True
|
||||
except RuntimeError:
|
||||
return False
|
||||
self.book = fitz.open(self.filename)
|
||||
|
||||
def generate_metadata(self):
|
||||
title = self.book.metadata['title']
|
||||
|
@@ -17,7 +17,6 @@
|
||||
# TODO
|
||||
# See if inserting chapters not in the toc.ncx can be avoided
|
||||
# Account for stylesheets... eventually
|
||||
# Everything needs logging
|
||||
|
||||
import os
|
||||
import zipfile
|
||||
@@ -40,6 +39,7 @@ class EPUB:
|
||||
self.zip_file = None
|
||||
self.file_list = None
|
||||
self.opf_dict = None
|
||||
self.cover_image_name = None
|
||||
self.split_chapters = {}
|
||||
|
||||
self.metadata = None
|
||||
@@ -89,7 +89,7 @@ class EPUB:
|
||||
return i
|
||||
|
||||
# If the file isn't found
|
||||
logging.error(filename + ' not found in ' + self.book_filename)
|
||||
logger.error(filename + ' not found in ' + self.book_filename)
|
||||
return False
|
||||
|
||||
def generate_toc(self):
|
||||
@@ -247,25 +247,19 @@ class EPUB:
|
||||
toc_chapters = [
|
||||
unquote(i[2].split('#')[0]) for i in self.content]
|
||||
|
||||
# TODO
|
||||
# This totally borks the order
|
||||
|
||||
last_valid_index = -2 # Yes, but why?
|
||||
for i in spine_final:
|
||||
if not i in toc_chapters:
|
||||
previous_chapter = spine_final[spine_final.index(i) - 1]
|
||||
try:
|
||||
spine_index = spine_final.index(i)
|
||||
if spine_index == 0: # Or chapter insertion circles back to the end
|
||||
previous_chapter_toc_index = -1
|
||||
else:
|
||||
previous_chapter = spine_final[spine_final.index(i) - 1]
|
||||
previous_chapter_toc_index = toc_chapters.index(previous_chapter)
|
||||
# In case of 2+ consecutive missing chapters
|
||||
last_valid_index = previous_chapter_toc_index
|
||||
except ValueError:
|
||||
last_valid_index += 1
|
||||
|
||||
# Chapters are currently named None
|
||||
# Blank chapters will later be removed
|
||||
# and the None will be replaced by a number
|
||||
toc_chapters.insert(
|
||||
previous_chapter_toc_index + 1, i)
|
||||
self.content.insert(
|
||||
last_valid_index + 1, [1, None, i])
|
||||
previous_chapter_toc_index + 1, [1, None, i])
|
||||
|
||||
# Parse split chapters as below
|
||||
# They can be picked up during the iteration through the toc
|
||||
@@ -334,28 +328,42 @@ class EPUB:
|
||||
chapter_title = i[1]
|
||||
if not chapter_title:
|
||||
chapter_title = unnamed_chapter_title
|
||||
unnamed_chapter_title += 1
|
||||
content_copy.append((
|
||||
i[0], str(chapter_title), i[2]))
|
||||
unnamed_chapter_title += 1
|
||||
self.content = content_copy
|
||||
|
||||
# TODO
|
||||
# This can probably be circumvented by shifting the extraction
|
||||
# to this module and simply getting the path to the cover
|
||||
|
||||
# Get cover image and put it in its place
|
||||
# I imagine this involves saying nasty things to it
|
||||
# There's no point shifting this to the parser
|
||||
# The performance increase is negligible
|
||||
cover_image = self.generate_book_cover()
|
||||
|
||||
if cover_image:
|
||||
cover_path = os.path.join(
|
||||
self.temp_dir, os.path.basename(self.book_filename)) + ' - cover'
|
||||
with open(cover_path, 'wb') as cover_temp:
|
||||
cover_temp.write(cover_image)
|
||||
|
||||
# There's probably some rationale to doing an insert here
|
||||
# But a replacement seems... neater
|
||||
self.content.insert(
|
||||
0, (1, 'Cover', f'<center><img src="{cover_path}" alt="Cover"></center>'))
|
||||
# This is probably stupid, but I can't stand the idea of
|
||||
# having to look at two book covers
|
||||
cover_replacement_conditions = (
|
||||
self.cover_image_name.lower() + '.jpg' in self.content[0][2].lower(),
|
||||
self.cover_image_name.lower() + '.png' in self.content[0][2].lower(),
|
||||
'cover' in self.content[0][1].lower())
|
||||
|
||||
if True in cover_replacement_conditions:
|
||||
logger.info(
|
||||
f'Replacing cover {cover_replacement_conditions}: {self.book_filename}')
|
||||
self.content[0] = (
|
||||
1, 'Cover',
|
||||
f'<center><img src="{cover_path}" alt="Cover"></center>')
|
||||
else:
|
||||
logger.info('Adding cover: ' + self.book_filename)
|
||||
self.content.insert(
|
||||
0,
|
||||
(1, 'Cover',
|
||||
f'<center><img src="{cover_path}" alt="Cover"></center>'))
|
||||
|
||||
def generate_metadata(self):
|
||||
book_metadata = self.opf_dict['package']['metadata']
|
||||
@@ -443,6 +451,8 @@ class EPUB:
|
||||
if i['@media-type'].split('/')[0] == 'image' and
|
||||
'cover' in i['@id']][0]
|
||||
book_cover = self.zip_file.read(self.find_file(cover_image))
|
||||
self.cover_image_name = os.path.splitext(
|
||||
os.path.basename(cover_image))[0]
|
||||
except:
|
||||
pass
|
||||
|
||||
|
@@ -124,8 +124,8 @@ class BookSorter:
|
||||
self.queue = Manager().Queue()
|
||||
self.processed_books = []
|
||||
|
||||
# if self.work_mode == 'addition':
|
||||
progress_object_generator()
|
||||
if self.work_mode == 'addition':
|
||||
progress_object_generator()
|
||||
|
||||
def database_hashes(self):
|
||||
all_hashes_and_paths = database.DatabaseFunctions(
|
||||
@@ -140,9 +140,6 @@ class BookSorter:
|
||||
i[0]: i[1] for i in all_hashes_and_paths}
|
||||
|
||||
def database_entry_for_book(self, file_hash):
|
||||
# TODO
|
||||
# This will probably look a whole lot better with a namedtuple
|
||||
|
||||
database_return = database.DatabaseFunctions(
|
||||
self.database_path).fetch_data(
|
||||
('Title', 'Author', 'Year', 'ISBN', 'Tags',
|
||||
@@ -187,7 +184,8 @@ class BookSorter:
|
||||
or os.path.exists(self.hashes_and_paths[file_md5])):
|
||||
|
||||
if not self.hashes_and_paths[file_md5] == filename:
|
||||
warning_string = f'{os.path.basename(filename)} is already in database'
|
||||
warning_string = (
|
||||
f'{os.path.basename(filename)} is already in database')
|
||||
logger.warning(warning_string)
|
||||
return
|
||||
|
||||
@@ -214,8 +212,9 @@ class BookSorter:
|
||||
|
||||
try:
|
||||
book_ref.read_book()
|
||||
except:
|
||||
logger.error('Error initializing: ' + filename)
|
||||
except Exception as e:
|
||||
this_error = f'Error initializing: {filename} {type(e).__name__} Arguments: {e.args}'
|
||||
logger.exception(this_error)
|
||||
return
|
||||
|
||||
this_book = {}
|
||||
@@ -227,8 +226,10 @@ class BookSorter:
|
||||
if self.work_mode == 'addition':
|
||||
try:
|
||||
metadata = book_ref.generate_metadata()
|
||||
except:
|
||||
logger.error('Metadata generation error: ' + filename)
|
||||
except Exception as e:
|
||||
this_error = (
|
||||
f'Metadata generation error: {filename} {type(e).__name__} Arguments: {e.args}')
|
||||
logger.exception(this_error)
|
||||
return
|
||||
|
||||
title = metadata.title
|
||||
@@ -255,16 +256,24 @@ class BookSorter:
|
||||
if self.work_mode == 'reading':
|
||||
try:
|
||||
book_breakdown = book_ref.generate_content()
|
||||
except KeyboardInterrupt:
|
||||
logger.error('Content generation error: ' + filename)
|
||||
except Exception as e:
|
||||
this_error = (
|
||||
f'Content generation error: {filename} {type(e).__name__} Arguments: {e.args}')
|
||||
logger.exception(this_error)
|
||||
return
|
||||
|
||||
toc = book_breakdown[0]
|
||||
content = book_breakdown[1]
|
||||
images_only = book_breakdown[2]
|
||||
|
||||
book_data = self.database_entry_for_book(file_md5)
|
||||
title = book_data[0]
|
||||
try:
|
||||
book_data = self.database_entry_for_book(file_md5)
|
||||
except TypeError:
|
||||
logger.error(
|
||||
f'Database error: {filename}. Re-add book to program')
|
||||
return
|
||||
|
||||
title = book_data[0].replace('&', '&&')
|
||||
author = book_data[1]
|
||||
year = book_data[2]
|
||||
isbn = book_data[3]
|
||||
|
@@ -521,30 +521,37 @@ class Tab(QtWidgets.QWidget):
|
||||
'center': QtCore.Qt.AlignCenter,
|
||||
'justify': QtCore.Qt.AlignJustify}
|
||||
|
||||
# Adjusted for books without covers
|
||||
current_position = self.metadata['position']['current_chapter']
|
||||
chapter_name = self.metadata['toc'][current_position - 1][1]
|
||||
|
||||
if current_position == 1 and chapter_name == 'Cover':
|
||||
block_format.setAlignment(
|
||||
QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter)
|
||||
else:
|
||||
block_format.setAlignment(alignment_dict[text_alignment])
|
||||
|
||||
# Also for padding
|
||||
# Using setViewPortMargins for this disables scrolling in the margins
|
||||
block_format.setLeftMargin(padding)
|
||||
block_format.setRightMargin(padding)
|
||||
|
||||
this_cursor = self.contentView.textCursor()
|
||||
this_cursor.movePosition(QtGui.QTextCursor.Start, 0, 1)
|
||||
this_cursor.setPosition(QtGui.QTextCursor.Start)
|
||||
|
||||
# Iterate over the entire document block by block
|
||||
# The document ends when the cursor position can no longer be incremented
|
||||
while True:
|
||||
# So this fixes the stupid repetitive iteration
|
||||
# I was doing over the entire thing
|
||||
# It also allows for all images to be center aligned.
|
||||
# Magic. *jazz hands*
|
||||
block_text = this_cursor.block().text().strip()
|
||||
try:
|
||||
# Object replacement char - Seems to work with images
|
||||
if ord(block_text) == 65532:
|
||||
block_format.setAlignment(
|
||||
QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter)
|
||||
else:
|
||||
raise TypeError
|
||||
except TypeError:
|
||||
block_format.setAlignment(alignment_dict[text_alignment])
|
||||
|
||||
# Iterate over the entire document block by block
|
||||
# The document ends when the cursor position can no longer be incremented
|
||||
old_position = this_cursor.position()
|
||||
this_cursor.mergeBlockFormat(block_format)
|
||||
this_cursor.movePosition(QtGui.QTextCursor.NextBlock, 0, 1)
|
||||
this_cursor.movePosition(
|
||||
QtGui.QTextCursor.NextBlock, QtGui.QTextCursor.MoveAnchor)
|
||||
|
||||
new_position = this_cursor.position()
|
||||
if old_position == new_position:
|
||||
break
|
||||
|
Reference in New Issue
Block a user