%PDF- %PDF-
| Direktori : /lib/calibre/calibre/gui2/dialogs/ |
| Current File : //lib/calibre/calibre/gui2/dialogs/match_books.py |
#!/usr/bin/env python3
__license__ = 'GPL v3'
__copyright__ = '2013, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
from qt.core import (Qt, QDialog, QAbstractItemView, QTableWidgetItem,
QByteArray, QApplication, QCursor, QTimer)
from calibre.gui2 import gprefs, error_dialog
from calibre.gui2.dialogs.match_books_ui import Ui_MatchBooks
from calibre.utils.icu import sort_key
class TableItem(QTableWidgetItem):
'''
A QTableWidgetItem that sorts on a separate string and uses ICU rules
'''
def __init__(self, val, sort, idx=0):
self.sort = sort
self.sort_idx = idx
QTableWidgetItem.__init__(self, val)
self.setFlags(Qt.ItemFlag.ItemIsEnabled|Qt.ItemFlag.ItemIsSelectable)
def __ge__(self, other):
l = sort_key(self.sort)
r = sort_key(other.sort)
if l > r:
return 1
if l == r:
return self.sort_idx >= other.sort_idx
return 0
def __lt__(self, other):
l = sort_key(self.sort)
r = sort_key(other.sort)
if l < r:
return 1
if l == r:
return self.sort_idx < other.sort_idx
return 0
class MatchBooks(QDialog, Ui_MatchBooks):
def __init__(self, gui, view, id_, row_index):
QDialog.__init__(self, gui, flags=Qt.WindowType.Window)
Ui_MatchBooks.__init__(self)
self.setupUi(self)
self.isClosed = False
self.books_table_column_widths = None
try:
self.books_table_column_widths = \
gprefs.get('match_books_dialog_books_table_widths', None)
geom = gprefs.get('match_books_dialog_geometry', None)
if geom:
QApplication.instance().safe_restore_geometry(self, QByteArray(geom))
except:
pass
self.search_text.initialize('match_books_dialog')
# Remove the help button from the window title bar
icon = self.windowIcon()
self.setWindowFlags(self.windowFlags()&(~Qt.WindowType.WindowContextHelpButtonHint))
self.setWindowIcon(icon)
self.device_db = view.model().db
self.library_db = gui.library_view.model().db
self.view = view
self.gui = gui
self.current_device_book_id = id_
self.current_device_book_index = row_index
self.current_library_book_id = None
# Set up the books table columns
self.books_table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.books_table.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
self.books_table.setColumnCount(3)
t = QTableWidgetItem(_('Title'))
self.books_table.setHorizontalHeaderItem(0, t)
t = QTableWidgetItem(_('Authors'))
self.books_table.setHorizontalHeaderItem(1, t)
t = QTableWidgetItem(ngettext("Series", 'Series', 1))
self.books_table.setHorizontalHeaderItem(2, t)
self.books_table_header_height = self.books_table.height()
self.books_table.cellDoubleClicked.connect(self.book_doubleclicked)
self.books_table.cellClicked.connect(self.book_clicked)
self.books_table.sortByColumn(0, Qt.SortOrder.AscendingOrder)
# get the standard table row height. Do this here because calling
# resizeRowsToContents can word wrap long cell contents, creating
# double-high rows
self.books_table.setRowCount(1)
self.books_table.setItem(0, 0, TableItem('A', ''))
self.books_table.resizeRowsToContents()
self.books_table_row_height = self.books_table.rowHeight(0)
self.books_table.setRowCount(0)
self.search_button.clicked.connect(self.do_search)
self.search_button.setDefault(False)
self.search_text.lineEdit().returnPressed.connect(self.return_pressed)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
self.ignore_next_key = False
search_text = self.device_db[self.current_device_book_id].title
search_text = search_text.replace('(', '\\(').replace(')', '\\)')
self.search_text.setText(search_text)
if search_text and len(self.library_db.new_api.all_book_ids()) < 8000:
QTimer.singleShot(0, self.search_button.click)
def return_pressed(self):
self.ignore_next_key = True
self.do_search()
def keyPressEvent(self, e):
if self.ignore_next_key:
self.ignore_next_key = False
else:
QDialog.keyPressEvent(self, e)
def do_search(self):
query = str(self.search_text.text())
if not query:
d = error_dialog(self.gui, _('Match books'),
_('You must enter a search expression into the search field'))
d.exec()
return
try:
self.search_button.setEnabled(False)
QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
books = self.library_db.data.search(query, return_matches=True)
self.books_table.setRowCount(len(books))
self.books_table.setSortingEnabled(False)
for row, b in enumerate(books):
mi = self.library_db.get_metadata(b, index_is_id=True, get_user_categories=False)
a = TableItem(mi.title, mi.title_sort)
a.setData(Qt.ItemDataRole.UserRole, b)
self.books_table.setItem(row, 0, a)
a = TableItem(' & '.join(mi.authors), mi.author_sort)
self.books_table.setItem(row, 1, a)
series = mi.format_field('series')[1]
if series is None:
series = ''
a = TableItem(series, mi.series, mi.series_index)
self.books_table.setItem(row, 2, a)
self.books_table.setRowHeight(row, self.books_table_row_height)
self.books_table.setSortingEnabled(True)
finally:
self.search_button.setEnabled(True)
QApplication.restoreOverrideCursor()
# Deal with sizing the table columns. Done here because the numbers are not
# correct until the first paint.
def resizeEvent(self, *args):
QDialog.resizeEvent(self, *args)
if self.books_table_column_widths is not None:
for c,w in enumerate(self.books_table_column_widths):
self.books_table.setColumnWidth(c, w)
else:
# the vertical scroll bar might not be rendered, so might not yet
# have a width. Assume 25. Not a problem because user-changed column
# widths will be remembered
w = self.books_table.width() - 25 - self.books_table.verticalHeader().width()
w /= self.books_table.columnCount()
for c in range(0, self.books_table.columnCount()):
self.books_table.setColumnWidth(c, w)
self.save_state()
def book_clicked(self, row, column):
self.book_selected = True
id_ = int(self.books_table.item(row, 0).data(Qt.ItemDataRole.UserRole))
self.current_library_book_id = id_
def book_doubleclicked(self, row, column):
self.book_clicked(row, column)
self.accept()
def save_state(self):
self.books_table_column_widths = []
for c in range(0, self.books_table.columnCount()):
self.books_table_column_widths.append(self.books_table.columnWidth(c))
gprefs['match_books_dialog_books_table_widths'] = self.books_table_column_widths
gprefs['match_books_dialog_geometry'] = bytearray(self.saveGeometry())
self.search_text.save_history()
def close(self):
self.save_state()
# clean up to prevent memory leaks
self.device_db = self.view = self.gui = None
def accept(self):
if not self.current_library_book_id:
d = error_dialog(self.gui, _('Match books'),
_('You must select a matching book'))
d.exec()
return
mi = self.library_db.get_metadata(self.current_library_book_id,
index_is_id=True, get_user_categories=False,
get_cover=True)
book = self.device_db[self.current_device_book_id]
book.smart_update(mi, replace_metadata=True)
self.gui.update_thumbnail(book)
book.in_library_waiting = True
self.view.model().current_changed(self.current_device_book_index,
self.current_device_book_index)
self.save_state()
QDialog.accept(self)
def reject(self):
self.close()
QDialog.reject(self)