Improve epub parsing, Table View for main library
This commit is contained in:
36
__main__.py
36
__main__.py
@@ -89,6 +89,9 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
# Initialize settings dialog
|
||||
self.settings_dialog = SettingsUI()
|
||||
|
||||
# Hide or show the main widget of the library
|
||||
self.tableView.setVisible(False)
|
||||
|
||||
# Empty variables that will be infested soon
|
||||
self.last_open_books = None
|
||||
self.last_open_tab = None
|
||||
@@ -176,22 +179,31 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
|
||||
# TODO
|
||||
# Associate this with the library switcher
|
||||
library_subclass = QtWidgets.QToolButton()
|
||||
library_subclass.setIcon(QtGui.QIcon.fromTheme('view-readermode'))
|
||||
library_subclass.setAutoRaise(True)
|
||||
library_subclass.setPopupMode(QtWidgets.QToolButton.InstantPopup)
|
||||
self.library_view_switch = QtWidgets.QToolButton()
|
||||
self.library_view_switch.setIcon(QtGui.QIcon.fromTheme('view-readermode'))
|
||||
self.library_view_switch.setAutoRaise(True)
|
||||
self.library_view_switch.setPopupMode(QtWidgets.QToolButton.InstantPopup)
|
||||
self.library_view_switch.triggered.connect(self.switch_library_view)
|
||||
|
||||
self.tabWidget.tabBar().setTabButton(0, QtWidgets.QTabBar.RightSide, library_subclass)
|
||||
self.tabWidget.tabBar().setTabButton(
|
||||
0, QtWidgets.QTabBar.RightSide, self.library_view_switch)
|
||||
self.library_view_switch.clicked.connect(self.switch_library_view)
|
||||
self.tabWidget.tabCloseRequested.connect(self.tab_close)
|
||||
|
||||
# Init display models
|
||||
self.lib_ref.generate_model('build')
|
||||
self.lib_ref.create_tablemodel() # TODO - Make this accompany other proxy model generations
|
||||
self.lib_ref.create_proxymodel()
|
||||
|
||||
# ListView
|
||||
self.listView.setGridSize(QtCore.QSize(175, 240))
|
||||
self.listView.setMouseTracking(True)
|
||||
self.listView.verticalScrollBar().setSingleStep(7)
|
||||
self.listView.doubleClicked.connect(self.list_doubleclick)
|
||||
self.listView.setItemDelegate(LibraryDelegate(self.temp_dir.path()))
|
||||
self.lib_ref.generate_model('build')
|
||||
self.lib_ref.create_proxymodel()
|
||||
|
||||
# TableView
|
||||
self.tableView.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
|
||||
|
||||
# Keyboard shortcuts
|
||||
self.ks_close_tab = QtWidgets.QShortcut(QtGui.QKeySequence('Ctrl+W'), self)
|
||||
@@ -290,6 +302,16 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
||||
msg_box.show()
|
||||
msg_box.exec_()
|
||||
|
||||
def switch_library_view(self):
|
||||
if self.listView.isVisible():
|
||||
self.listView.setVisible(False)
|
||||
self.tableView.setVisible(True)
|
||||
self.libraryToolBar.sortingBoxAction.setVisible(False)
|
||||
else:
|
||||
self.listView.setVisible(True)
|
||||
self.tableView.setVisible(False)
|
||||
self.libraryToolBar.sortingBoxAction.setVisible(True)
|
||||
|
||||
def tab_switch(self):
|
||||
if self.tabWidget.currentIndex() == 0:
|
||||
|
||||
|
18
library.py
18
library.py
@@ -5,7 +5,7 @@ import pickle
|
||||
import database
|
||||
|
||||
from PyQt5 import QtWidgets, QtGui, QtCore
|
||||
from widgets import MyAbsModel
|
||||
from widgets import LibraryItemModel, LibraryTableModel
|
||||
|
||||
|
||||
class Library:
|
||||
@@ -13,6 +13,8 @@ class Library:
|
||||
self.parent_window = parent
|
||||
self.view_model = None
|
||||
self.proxy_model = None
|
||||
self.table_model = None
|
||||
self.table_rows = []
|
||||
|
||||
def generate_model(self, mode, parsed_books=None):
|
||||
# The QlistView widget needs to be populated
|
||||
@@ -20,7 +22,7 @@ class Library:
|
||||
# because I kinda sorta NEED the match() method
|
||||
|
||||
if mode == 'build':
|
||||
self.view_model = MyAbsModel()
|
||||
self.view_model = LibraryItemModel()
|
||||
|
||||
books = database.DatabaseFunctions(
|
||||
self.parent_window.database_path).fetch_data(
|
||||
@@ -65,7 +67,7 @@ class Library:
|
||||
author = i[2]
|
||||
year = i[3]
|
||||
path = i[4]
|
||||
tags = i[6]
|
||||
tags = i[7]
|
||||
cover = i[9]
|
||||
|
||||
position = i[5]
|
||||
@@ -120,6 +122,16 @@ class Library:
|
||||
item.setIcon(QtGui.QIcon(img_pixmap))
|
||||
self.view_model.appendRow(item)
|
||||
|
||||
# Path is just being sent. It is not being displayed
|
||||
self.table_rows.append(
|
||||
(title, author, year, tags, path))
|
||||
|
||||
def create_tablemodel(self):
|
||||
table_header = ['Title', 'Author', 'Year', 'Tags']
|
||||
self.table_rows.sort(key=lambda x: x[0])
|
||||
self.table_model = LibraryTableModel(table_header, self.table_rows)
|
||||
self.parent_window.tableView.setModel(self.table_model)
|
||||
|
||||
def create_proxymodel(self):
|
||||
self.proxy_model = QtCore.QSortFilterProxyModel()
|
||||
self.proxy_model.setSourceModel(self.view_model)
|
||||
|
@@ -12,6 +12,7 @@ import os
|
||||
import re
|
||||
import zipfile
|
||||
import collections
|
||||
from urllib.parse import unquote
|
||||
|
||||
import ebooklib.epub
|
||||
|
||||
@@ -101,37 +102,36 @@ class ParseEPUB:
|
||||
return None
|
||||
|
||||
def get_contents(self):
|
||||
# Extract all contents to a temporary directory
|
||||
# for relative path lookup voodoo
|
||||
extract_path = os.path.join(self.temp_dir, self.file_md5)
|
||||
zipfile.ZipFile(self.filename).extractall(extract_path)
|
||||
|
||||
contents = collections.OrderedDict()
|
||||
|
||||
def flatten_chapter(toc_element):
|
||||
def flatten_section(toc_element):
|
||||
output_list = []
|
||||
for i in toc_element:
|
||||
if isinstance(i, (tuple, list)):
|
||||
output_list.extend(flatten_chapter(i))
|
||||
output_list.extend(flatten_section(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())
|
||||
flattened = flatten_section(i)
|
||||
|
||||
for j in flattened:
|
||||
title = j.title
|
||||
href = unquote(j.href)
|
||||
try:
|
||||
content = self.book.get_item_with_href(href).get_content()
|
||||
contents[title] = content.decode()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# contents[title] = composite_chapter_content
|
||||
else:
|
||||
title = i.title
|
||||
href = i.href
|
||||
href = unquote(i.href)
|
||||
try:
|
||||
content = self.book.get_item_with_href(href).get_content()
|
||||
if content:
|
||||
@@ -139,7 +139,7 @@ class ParseEPUB:
|
||||
else:
|
||||
raise AttributeError
|
||||
except AttributeError:
|
||||
contents[title] = ''
|
||||
contents[title] = 'Parse Error'
|
||||
|
||||
# Special settings that have to be returned with the file
|
||||
# Referenced in sorter.py
|
||||
|
@@ -1,8 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'resources/main.ui'
|
||||
# Form implementation generated from reading ui file 'raw/main.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.9.1
|
||||
# Created by: PyQt5 UI code generator 5.9.2
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
@@ -23,10 +23,8 @@ class Ui_MainWindow(object):
|
||||
self.tabWidget.setObjectName("tabWidget")
|
||||
self.tab = QtWidgets.QWidget()
|
||||
self.tab.setObjectName("tab")
|
||||
self.gridLayout_2 = QtWidgets.QGridLayout(self.tab)
|
||||
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.tab)
|
||||
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
||||
self.listView = QtWidgets.QListView(self.tab)
|
||||
self.listView.setFrameShape(QtWidgets.QFrame.NoFrame)
|
||||
self.listView.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
||||
@@ -40,8 +38,20 @@ class Ui_MainWindow(object):
|
||||
self.listView.setUniformItemSizes(True)
|
||||
self.listView.setWordWrap(True)
|
||||
self.listView.setObjectName("listView")
|
||||
self.verticalLayout.addWidget(self.listView)
|
||||
self.gridLayout_2.addLayout(self.verticalLayout, 0, 0, 1, 1)
|
||||
self.horizontalLayout_2.addWidget(self.listView)
|
||||
self.tableView = QtWidgets.QTableView(self.tab)
|
||||
self.tableView.setFrameShape(QtWidgets.QFrame.NoFrame)
|
||||
self.tableView.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
|
||||
self.tableView.setEditTriggers(QtWidgets.QAbstractItemView.DoubleClicked|QtWidgets.QAbstractItemView.EditKeyPressed)
|
||||
self.tableView.setAlternatingRowColors(True)
|
||||
self.tableView.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||
self.tableView.setGridStyle(QtCore.Qt.NoPen)
|
||||
self.tableView.setSortingEnabled(True)
|
||||
self.tableView.setWordWrap(False)
|
||||
self.tableView.setObjectName("tableView")
|
||||
self.tableView.horizontalHeader().setVisible(True)
|
||||
self.tableView.verticalHeader().setVisible(False)
|
||||
self.horizontalLayout_2.addWidget(self.tableView)
|
||||
self.tabWidget.addTab(self.tab, "")
|
||||
self.horizontalLayout.addWidget(self.tabWidget)
|
||||
self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1)
|
||||
@@ -64,4 +74,3 @@ class Ui_MainWindow(object):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
MainWindow.setWindowTitle(_translate("MainWindow", "Lector"))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Library"))
|
||||
|
||||
|
@@ -29,50 +29,80 @@
|
||||
<attribute name="title">
|
||||
<string>Library</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QListView" name="listView">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="showDropIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="movement">
|
||||
<enum>QListView::Static</enum>
|
||||
</property>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="resizeMode">
|
||||
<enum>QListView::Fixed</enum>
|
||||
</property>
|
||||
<property name="layoutMode">
|
||||
<enum>QListView::SinglePass</enum>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="viewMode">
|
||||
<enum>QListView::IconMode</enum>
|
||||
</property>
|
||||
<property name="uniformItemSizes">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QListView" name="listView">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="showDropIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="movement">
|
||||
<enum>QListView::Static</enum>
|
||||
</property>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="resizeMode">
|
||||
<enum>QListView::Fixed</enum>
|
||||
</property>
|
||||
<property name="layoutMode">
|
||||
<enum>QListView::SinglePass</enum>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="viewMode">
|
||||
<enum>QListView::IconMode</enum>
|
||||
</property>
|
||||
<property name="uniformItemSizes">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableView" name="tableView">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContentsOnFirstShow</enum>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="gridStyle">
|
||||
<enum>Qt::NoPen</enum>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderVisible">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
@@ -132,13 +132,18 @@ class BookSorter:
|
||||
# None values are accounted for here
|
||||
book_ref.read_book()
|
||||
if book_ref.book:
|
||||
|
||||
title = book_ref.get_title().title()
|
||||
|
||||
author = book_ref.get_author()
|
||||
if not author:
|
||||
author = 'Unknown'
|
||||
year = book_ref.get_year()
|
||||
if not year:
|
||||
|
||||
try:
|
||||
year = int(book_ref.get_year())
|
||||
except (TypeError, ValueError):
|
||||
year = 9999
|
||||
|
||||
isbn = book_ref.get_isbn()
|
||||
|
||||
# Different modes require different values
|
||||
|
48
widgets.py
48
widgets.py
@@ -297,7 +297,7 @@ class LibraryToolBar(QtWidgets.QToolBar):
|
||||
|
||||
# Add widgets
|
||||
self.addWidget(spacer)
|
||||
self.addWidget(self.sortingBox)
|
||||
self.sortingBoxAction = self.addWidget(self.sortingBox)
|
||||
self.addWidget(self.searchBar)
|
||||
|
||||
|
||||
@@ -709,7 +709,49 @@ class LibraryDelegate(QtWidgets.QStyledItemDelegate):
|
||||
painter.drawPixmap(x_draw, y_draw, read_icon)
|
||||
|
||||
|
||||
class MyAbsModel(QtGui.QStandardItemModel, QtCore.QAbstractItemModel):
|
||||
class LibraryItemModel(QtGui.QStandardItemModel, QtCore.QAbstractItemModel):
|
||||
def __init__(self, parent=None):
|
||||
# We're using this to be able to access the match() method
|
||||
super(MyAbsModel, self).__init__(parent)
|
||||
super(LibraryItemModel, self).__init__(parent)
|
||||
|
||||
class LibraryTableModel(QtCore.QAbstractTableModel):
|
||||
# TODO
|
||||
# Speed up sorting
|
||||
# Associate with a proxy model to enable searching
|
||||
# Double clicking
|
||||
# Auto resize with emphasis on Name
|
||||
# Hide path but send it anyway
|
||||
|
||||
def __init__(self, header_data, display_data, parent=None):
|
||||
super(LibraryTableModel, self).__init__(parent)
|
||||
self.header_data = header_data
|
||||
self.display_data = display_data
|
||||
|
||||
def rowCount(self, parent):
|
||||
return len(self.display_data)
|
||||
|
||||
def columnCount(self, parent):
|
||||
return len(self.header_data)
|
||||
|
||||
def data(self, index, role):
|
||||
if not index.isValid():
|
||||
return None
|
||||
|
||||
if role == QtCore.Qt.DisplayRole:
|
||||
value = self.display_data[index.row()][index.column()]
|
||||
return value
|
||||
else:
|
||||
return QtCore.QVariant()
|
||||
|
||||
def headerData(self, col, orientation, role):
|
||||
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
|
||||
return self.header_data[col]
|
||||
return None
|
||||
|
||||
def sort(self, col, order):
|
||||
# self.emit(SIGNAL("layoutAboutToBeChanged()"))
|
||||
self.display_data.sort(key=lambda x: x[col])
|
||||
if order == QtCore.Qt.DescendingOrder:
|
||||
self.display_data.sort(key=lambda x: x[col], reverse=True)
|
||||
|
||||
# self.emit(SIGNAL("layoutChanged()"))
|
||||
|
Reference in New Issue
Block a user