mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement the images report
This commit is contained in:
parent
c7b434dd07
commit
484bfaed72
@ -55,7 +55,8 @@ def file_data(container):
|
|||||||
yield File(name, posixpath.dirname(name), posixpath.basename(name), safe_size(container, name),
|
yield File(name, posixpath.dirname(name), posixpath.basename(name), safe_size(container, name),
|
||||||
get_category(name, container.mime_map.get(name, '')))
|
get_category(name, container.mime_map.get(name, '')))
|
||||||
|
|
||||||
Image = namedtuple('Image', 'name mime_type usage size width height')
|
Image = namedtuple('Image', 'name mime_type usage size basename width height')
|
||||||
|
|
||||||
L = namedtuple('Location', 'name line_number offset word')
|
L = namedtuple('Location', 'name line_number offset word')
|
||||||
def Location(name, line_number=None, offset=0, word=None):
|
def Location(name, line_number=None, offset=0, word=None):
|
||||||
return L(name, line_number, offset, word)
|
return L(name, line_number, offset, word)
|
||||||
@ -81,7 +82,7 @@ def link_data(container):
|
|||||||
for name, mt in container.mime_map.iteritems():
|
for name, mt in container.mime_map.iteritems():
|
||||||
if mt.startswith('image/') and container.exists(name):
|
if mt.startswith('image/') and container.exists(name):
|
||||||
image_data.append(Image(name, mt, sort_locations(image_usage.get(name, set())), safe_size(container, name),
|
image_data.append(Image(name, mt, sort_locations(image_usage.get(name, set())), safe_size(container, name),
|
||||||
*safe_img_data(container, name, mt)))
|
posixpath.basename(name), *safe_img_data(container, name, mt)))
|
||||||
return tuple(image_data)
|
return tuple(image_data)
|
||||||
|
|
||||||
def gather_data(container):
|
def gather_data(container):
|
||||||
|
@ -130,6 +130,8 @@ class Boss(QObject):
|
|||||||
self.gui.manage_fonts.embed_all_fonts.connect(self.manage_fonts_embed)
|
self.gui.manage_fonts.embed_all_fonts.connect(self.manage_fonts_embed)
|
||||||
self.gui.manage_fonts.subset_all_fonts.connect(self.manage_fonts_subset)
|
self.gui.manage_fonts.subset_all_fonts.connect(self.manage_fonts_subset)
|
||||||
self.gui.reports.edit_requested.connect(self.reports_edit_requested)
|
self.gui.reports.edit_requested.connect(self.reports_edit_requested)
|
||||||
|
self.gui.reports.refresh_starting.connect(self.commit_all_editors_to_container)
|
||||||
|
self.gui.reports.delete_requested.connect(self.delete_requested)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def currently_editing(self):
|
def currently_editing(self):
|
||||||
|
@ -9,20 +9,24 @@ __copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
from threading import Thread
|
from threading import Thread
|
||||||
from future_builtins import map
|
from future_builtins import map
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
from PyQt5.Qt import (
|
from PyQt5.Qt import (
|
||||||
QSize, QStackedLayout, QLabel, QVBoxLayout, Qt, QWidget, pyqtSignal,
|
QSize, QStackedLayout, QLabel, QVBoxLayout, Qt, QWidget, pyqtSignal,
|
||||||
QAbstractTableModel, QTableView, QSortFilterProxyModel, QIcon, QListWidget,
|
QAbstractTableModel, QTableView, QSortFilterProxyModel, QIcon, QListWidget,
|
||||||
QListWidgetItem, QLineEdit, QStackedWidget, QSplitter, QByteArray)
|
QListWidgetItem, QLineEdit, QStackedWidget, QSplitter, QByteArray, QPixmap,
|
||||||
|
QStyledItemDelegate, QModelIndex, QRect, QStyle, QPalette, QTimer, QMenu)
|
||||||
|
|
||||||
from calibre import human_readable
|
from calibre import human_readable, fit_image
|
||||||
from calibre.ebooks.oeb.polish.report import gather_data, Location
|
from calibre.ebooks.oeb.polish.report import gather_data, Location
|
||||||
from calibre.gui2 import error_dialog
|
from calibre.gui2 import error_dialog, question_dialog
|
||||||
from calibre.gui2.tweak_book import current_container, tprefs
|
from calibre.gui2.tweak_book import current_container, tprefs
|
||||||
from calibre.gui2.tweak_book.widgets import Dialog
|
from calibre.gui2.tweak_book.widgets import Dialog
|
||||||
from calibre.gui2.progress_indicator import ProgressIndicator
|
from calibre.gui2.progress_indicator import ProgressIndicator
|
||||||
from calibre.utils.icu import primary_contains, numeric_sort_key
|
from calibre.utils.icu import primary_contains, numeric_sort_key
|
||||||
|
|
||||||
|
# Utils {{{
|
||||||
|
|
||||||
def read_state(name, default=None):
|
def read_state(name, default=None):
|
||||||
data = tprefs.get('reports-ui-state')
|
data = tprefs.get('reports-ui-state')
|
||||||
if data is None:
|
if data is None:
|
||||||
@ -37,7 +41,7 @@ def save_state(name, val):
|
|||||||
tprefs['reports-ui-state'] = data = {}
|
tprefs['reports-ui-state'] = data = {}
|
||||||
data[name] = val
|
data[name] = val
|
||||||
|
|
||||||
class ProxyModel(QSortFilterProxyModel): # {{{
|
class ProxyModel(QSortFilterProxyModel):
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QSortFilterProxyModel.__init__(self, parent)
|
QSortFilterProxyModel.__init__(self, parent)
|
||||||
@ -64,7 +68,6 @@ class ProxyModel(QSortFilterProxyModel): # {{{
|
|||||||
if orientation == Qt.Vertical and role == Qt.DisplayRole:
|
if orientation == Qt.Vertical and role == Qt.DisplayRole:
|
||||||
return section + 1
|
return section + 1
|
||||||
return QSortFilterProxyModel.headerData(self, section, orientation, role)
|
return QSortFilterProxyModel.headerData(self, section, orientation, role)
|
||||||
# }}}
|
|
||||||
|
|
||||||
class FileCollection(QAbstractTableModel):
|
class FileCollection(QAbstractTableModel):
|
||||||
|
|
||||||
@ -101,7 +104,68 @@ class FileCollection(QAbstractTableModel):
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class FilesView(QTableView):
|
||||||
|
|
||||||
|
double_clicked = pyqtSignal(object)
|
||||||
|
delete_requested = pyqtSignal(object, object)
|
||||||
|
|
||||||
|
def __init__(self, model, parent=None):
|
||||||
|
QTableView.__init__(self, parent)
|
||||||
|
self.setSelectionBehavior(self.SelectRows), self.setSelectionMode(self.ExtendedSelection)
|
||||||
|
self.setSortingEnabled(True)
|
||||||
|
self.proxy = p = ProxyModel(self)
|
||||||
|
p.setSourceModel(model)
|
||||||
|
self.setModel(p)
|
||||||
|
self.doubleClicked.connect(self._double_clicked)
|
||||||
|
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||||
|
self.customContextMenuRequested.connect(self.show_context_menu)
|
||||||
|
|
||||||
|
def customize_context_menu(self, menu, selected_locations, current_location):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _double_clicked(self, index):
|
||||||
|
index = self.proxy.mapToSource(index)
|
||||||
|
if index.isValid():
|
||||||
|
self.double_clicked.emit(index)
|
||||||
|
|
||||||
|
def keyPressEvent(self, ev):
|
||||||
|
if ev.key() == Qt.Key_Delete:
|
||||||
|
self.delete_selected()
|
||||||
|
ev.accept()
|
||||||
|
return
|
||||||
|
return QTableView.keyPressEvent(self, ev)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def selected_locations(self):
|
||||||
|
return filter(None, (self.proxy.sourceModel().location(self.proxy.mapToSource(index)) for index in self.selectionModel().selectedIndexes()))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_location(self):
|
||||||
|
index = self.selectionModel().currentIndex()
|
||||||
|
return self.proxy.sourceModel().location(self.proxy.mapToSource(index))
|
||||||
|
|
||||||
|
def delete_selected(self):
|
||||||
|
locations = self.selected_locations
|
||||||
|
if locations:
|
||||||
|
names = {l.name for l in locations}
|
||||||
|
spine_names = {n for n, l in current_container().spine_names}
|
||||||
|
spine_items = spine_names.intersection(names)
|
||||||
|
other_items = names - spine_names
|
||||||
|
self.delete_requested.emit(spine_items, other_items)
|
||||||
|
|
||||||
|
def show_context_menu(self, pos):
|
||||||
|
pos = self.viewport().mapToGlobal(pos)
|
||||||
|
locations = self.selected_locations
|
||||||
|
m = QMenu(self)
|
||||||
|
if locations:
|
||||||
|
m.addAction(_('Delete selected files'), self.delete_selected)
|
||||||
|
self.customize_context_menu(m, locations, self.current_location)
|
||||||
|
if len(m.actions()) > 0:
|
||||||
|
m.exec_(pos)
|
||||||
|
# }}}
|
||||||
|
|
||||||
# Files {{{
|
# Files {{{
|
||||||
|
|
||||||
class FilesModel(FileCollection):
|
class FilesModel(FileCollection):
|
||||||
|
|
||||||
COLUMN_HEADERS = (_('Folder'), _('Name'), _('Size (KB)'), _('Type'))
|
COLUMN_HEADERS = (_('Folder'), _('Name'), _('Size (KB)'), _('Type'))
|
||||||
@ -149,6 +213,7 @@ class FilesModel(FileCollection):
|
|||||||
class FilesWidget(QWidget):
|
class FilesWidget(QWidget):
|
||||||
|
|
||||||
edit_requested = pyqtSignal(object)
|
edit_requested = pyqtSignal(object)
|
||||||
|
delete_requested = pyqtSignal(object, object)
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QWidget.__init__(self, parent)
|
QWidget.__init__(self, parent)
|
||||||
@ -157,16 +222,12 @@ class FilesWidget(QWidget):
|
|||||||
self.filter_edit = e = QLineEdit(self)
|
self.filter_edit = e = QLineEdit(self)
|
||||||
l.addWidget(e)
|
l.addWidget(e)
|
||||||
e.setPlaceholderText(_('Filter'))
|
e.setPlaceholderText(_('Filter'))
|
||||||
self.files = f = QTableView(self)
|
|
||||||
f.setSelectionBehavior(f.SelectRows), f.setSelectionMode(f.SingleSelection)
|
|
||||||
f.doubleClicked.connect(self.double_clicked)
|
|
||||||
self.model = m = FilesModel(self)
|
self.model = m = FilesModel(self)
|
||||||
self.proxy = p = ProxyModel(self)
|
self.files = f = FilesView(m, self)
|
||||||
e.textChanged.connect(p.filter_text)
|
f.delete_requested.connect(self.delete_requested)
|
||||||
p.setSourceModel(m)
|
f.double_clicked.connect(self.double_clicked)
|
||||||
f.setModel(p)
|
e.textChanged.connect(f.proxy.filter_text)
|
||||||
l.addWidget(f)
|
l.addWidget(f)
|
||||||
f.setSortingEnabled(True)
|
|
||||||
|
|
||||||
self.summary = s = QLabel(self)
|
self.summary = s = QLabel(self)
|
||||||
l.addWidget(s)
|
l.addWidget(s)
|
||||||
@ -178,12 +239,13 @@ class FilesWidget(QWidget):
|
|||||||
|
|
||||||
def __call__(self, data):
|
def __call__(self, data):
|
||||||
self.model(data)
|
self.model(data)
|
||||||
|
self.filter_edit.clear()
|
||||||
m = self.model
|
m = self.model
|
||||||
self.summary.setText(_('Total uncompressed size of all files: {0} :: Images: {1} :: Fonts: {2}').format(*map(
|
self.summary.setText(_('Total uncompressed size of all files: {0} :: Images: {1} :: Fonts: {2}').format(*map(
|
||||||
human_readable, (m.total_size, m.images_size, m.fonts_size))))
|
human_readable, (m.total_size, m.images_size, m.fonts_size))))
|
||||||
|
|
||||||
def double_clicked(self, index):
|
def double_clicked(self, index):
|
||||||
location = self.model.location(self.proxy.mapToSource(index))
|
location = self.model.location(index)
|
||||||
if location is not None:
|
if location is not None:
|
||||||
self.edit_requested.emit(location)
|
self.edit_requested.emit(location)
|
||||||
|
|
||||||
@ -192,52 +254,74 @@ class FilesWidget(QWidget):
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class ImagesModel(QAbstractTableModel):
|
# Images {{{
|
||||||
|
|
||||||
COLUMN_HEADERS = [_('Name'), _('Size (KB)'), _('Times used'), _('Width'), _('Height'), _('Image')]
|
class ImagesDelegate(QStyledItemDelegate):
|
||||||
|
|
||||||
|
MARGIN = 5
|
||||||
|
|
||||||
|
def __init__(self, *args):
|
||||||
|
QStyledItemDelegate.__init__(self, *args)
|
||||||
|
self.cache = {}
|
||||||
|
|
||||||
|
def sizeHint(self, option, index):
|
||||||
|
ans = QStyledItemDelegate.sizeHint(self, option, index)
|
||||||
|
entry = index.data(Qt.UserRole)
|
||||||
|
if entry is None:
|
||||||
|
return ans
|
||||||
|
th = self.parent().thumbnail_height
|
||||||
|
width, height = min(th, entry.width), min(th, entry.height)
|
||||||
|
m = self.MARGIN * 2
|
||||||
|
return QSize(max(width + m, ans.width()), height + m + self.MARGIN + ans.height())
|
||||||
|
|
||||||
|
def paint(self, painter, option, index):
|
||||||
|
QStyledItemDelegate.paint(self, painter, option, QModelIndex())
|
||||||
|
entry = index.data(Qt.UserRole)
|
||||||
|
if entry is None:
|
||||||
|
return
|
||||||
|
painter.save()
|
||||||
|
th = self.parent().thumbnail_height
|
||||||
|
k = (th, entry.name)
|
||||||
|
pmap = self.cache.get(k)
|
||||||
|
if pmap is None:
|
||||||
|
pmap = self.cache[k] = self.pixmap(th, entry)
|
||||||
|
if pmap.isNull():
|
||||||
|
bottom = option.rect.top()
|
||||||
|
else:
|
||||||
|
m = 2 * self.MARGIN
|
||||||
|
x = option.rect.left() + (option.rect.width() - m - pmap.width()) // 2
|
||||||
|
painter.drawPixmap(x, option.rect.top() + self.MARGIN, pmap)
|
||||||
|
bottom = m + pmap.height() + option.rect.top()
|
||||||
|
rect = QRect(option.rect.left(), bottom, option.rect.width(), option.rect.bottom() - bottom)
|
||||||
|
if option.state & QStyle.State_Selected:
|
||||||
|
painter.setPen(self.parent().palette().color(QPalette.HighlightedText))
|
||||||
|
painter.drawText(rect, Qt.AlignHCenter | Qt.AlignVCenter, entry.basename)
|
||||||
|
painter.restore()
|
||||||
|
|
||||||
|
def pixmap(self, thumbnail_height, entry):
|
||||||
|
pmap = QPixmap(current_container().name_to_abspath(entry.name)) if entry.width > 0 and entry.height > 0 else QPixmap()
|
||||||
|
scaled, width, height = fit_image(entry.width, entry.height, thumbnail_height, thumbnail_height)
|
||||||
|
if scaled and not pmap.isNull():
|
||||||
|
pmap = pmap.scaled(width, height, transformMode=Qt.SmoothTransformation)
|
||||||
|
return pmap
|
||||||
|
|
||||||
|
|
||||||
|
class ImagesModel(FileCollection):
|
||||||
|
|
||||||
|
COLUMN_HEADERS = [_('Image'), _('Size (KB)'), _('Times used'), _('Resolution')]
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
self.files = self.sort_keys = ()
|
FileCollection.__init__(self, parent)
|
||||||
self.total_size = 0
|
|
||||||
QAbstractTableModel.__init__(self, parent)
|
|
||||||
|
|
||||||
def columnCount(self, parent=None):
|
|
||||||
return len(self.COLUMN_HEADERS)
|
|
||||||
|
|
||||||
def rowCount(self, parent=None):
|
|
||||||
return len(self.files)
|
|
||||||
|
|
||||||
def headerData(self, section, orientation, role=Qt.DisplayRole):
|
|
||||||
if role == Qt.DisplayRole and orientation == Qt.Horizontal:
|
|
||||||
try:
|
|
||||||
return self.COLUMN_HEADERS[section]
|
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
return QAbstractTableModel.headerData(self, section, orientation, role)
|
|
||||||
|
|
||||||
def __call__(self, data):
|
def __call__(self, data):
|
||||||
self.beginResetModel()
|
self.beginResetModel()
|
||||||
self.files = data['files']
|
self.files = data['images']
|
||||||
self.total_size = sum(map(itemgetter(3), self.files))
|
self.total_size = sum(map(itemgetter(3), self.files))
|
||||||
self.images_size = sum(map(itemgetter(3), (f for f in self.files if f.category == 'image')))
|
|
||||||
self.fonts_size = sum(map(itemgetter(3), (f for f in self.files if f.category == 'font')))
|
|
||||||
psk = numeric_sort_key
|
psk = numeric_sort_key
|
||||||
self.sort_keys = tuple((psk(entry.dir), psk(entry.basename), entry.size, psk(self.CATEGORY_NAMES.get(entry.category, '')))
|
self.sort_keys = tuple((psk(entry.basename), entry.size, len(entry.usage), (entry.width, entry.height))
|
||||||
for entry in self.files)
|
for entry in self.files)
|
||||||
self.endResetModel()
|
self.endResetModel()
|
||||||
|
|
||||||
def sort_key(self, row, col):
|
|
||||||
try:
|
|
||||||
return self.sort_keys[row][col]
|
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def name(self, index):
|
|
||||||
try:
|
|
||||||
return self.files[index.row()].name
|
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def data(self, index, role=Qt.DisplayRole):
|
def data(self, index, role=Qt.DisplayRole):
|
||||||
if role == Qt.DisplayRole:
|
if role == Qt.DisplayRole:
|
||||||
col = index.column()
|
col = index.column()
|
||||||
@ -246,20 +330,77 @@ class ImagesModel(QAbstractTableModel):
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
return None
|
return None
|
||||||
if col == 0:
|
if col == 0:
|
||||||
return entry.dir
|
|
||||||
if col == 1:
|
|
||||||
return entry.basename
|
return entry.basename
|
||||||
if col == 2:
|
if col == 1:
|
||||||
sz = entry.size / 1024.
|
sz = entry.size / 1024.
|
||||||
return ('%.2f' % sz if int(sz) != sz else type('')(sz))
|
return ('%.2f' % sz if int(sz) != sz else type('')(sz))
|
||||||
|
if col == 2:
|
||||||
|
return type('')(len(entry.usage))
|
||||||
if col == 3:
|
if col == 3:
|
||||||
return self.CATEGORY_NAMES.get(entry.category)
|
return '%d x %d' % (entry.width, entry.height)
|
||||||
|
if role == Qt.UserRole:
|
||||||
|
try:
|
||||||
|
return self.files[index.row()]
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ImagesWidget(QWidget):
|
||||||
|
|
||||||
|
edit_requested = pyqtSignal(object)
|
||||||
|
delete_requested = pyqtSignal(object, object)
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QWidget.__init__(self, parent)
|
||||||
|
self.l = l = QVBoxLayout(self)
|
||||||
|
self.thumbnail_height = 64
|
||||||
|
|
||||||
|
self.filter_edit = e = QLineEdit(self)
|
||||||
|
l.addWidget(e)
|
||||||
|
e.setPlaceholderText(_('Filter'))
|
||||||
|
self.model = m = ImagesModel(self)
|
||||||
|
self.files = f = FilesView(m, self)
|
||||||
|
f.customize_context_menu = self.customize_context_menu
|
||||||
|
f.delete_requested.connect(self.delete_requested)
|
||||||
|
f.horizontalHeader().sortIndicatorChanged.connect(self.resize_to_contents)
|
||||||
|
self.delegate = ImagesDelegate(self)
|
||||||
|
f.setItemDelegateForColumn(0, self.delegate)
|
||||||
|
f.double_clicked.connect(self.double_clicked)
|
||||||
|
e.textChanged.connect(f.proxy.filter_text)
|
||||||
|
l.addWidget(f)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.files.horizontalHeader().restoreState(read_state('image-files-table'))
|
||||||
|
except TypeError:
|
||||||
|
self.files.sortByColumn(0, Qt.AscendingOrder)
|
||||||
|
|
||||||
|
def __call__(self, data):
|
||||||
|
self.model(data)
|
||||||
|
self.filter_edit.clear()
|
||||||
|
self.delegate.cache.clear()
|
||||||
|
self.files.resizeRowsToContents()
|
||||||
|
|
||||||
|
def resize_to_contents(self, *args):
|
||||||
|
QTimer.singleShot(0, self.files.resizeRowsToContents)
|
||||||
|
|
||||||
|
def double_clicked(self, index):
|
||||||
|
location = self.model.location(index)
|
||||||
|
if location is not None:
|
||||||
|
self.edit_requested.emit(location)
|
||||||
|
|
||||||
|
def customize_context_menu(self, menu, selected_locations, current_location):
|
||||||
|
if current_location is not None:
|
||||||
|
menu.addAction(_('Edit the image: %s') % current_location.name, partial(self.edit_requested.emit, current_location))
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
save_state('image-files-table', bytearray(self.files.horizontalHeader().saveState()))
|
||||||
|
# }}}
|
||||||
|
|
||||||
# Wrapper UI {{{
|
# Wrapper UI {{{
|
||||||
class ReportsWidget(QWidget):
|
class ReportsWidget(QWidget):
|
||||||
|
|
||||||
edit_requested = pyqtSignal(object)
|
edit_requested = pyqtSignal(object)
|
||||||
|
delete_requested = pyqtSignal(object, object)
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QWidget.__init__(self, parent)
|
QWidget.__init__(self, parent)
|
||||||
@ -275,9 +416,16 @@ class ReportsWidget(QWidget):
|
|||||||
|
|
||||||
self.files = f = FilesWidget(self)
|
self.files = f = FilesWidget(self)
|
||||||
f.edit_requested.connect(self.edit_requested)
|
f.edit_requested.connect(self.edit_requested)
|
||||||
|
f.delete_requested.connect(self.delete_requested)
|
||||||
s.addWidget(f)
|
s.addWidget(f)
|
||||||
QListWidgetItem(_('Files'), r)
|
QListWidgetItem(_('Files'), r)
|
||||||
|
|
||||||
|
self.images = i = ImagesWidget(self)
|
||||||
|
i.edit_requested.connect(self.edit_requested)
|
||||||
|
i.delete_requested.connect(self.delete_requested)
|
||||||
|
s.addWidget(i)
|
||||||
|
QListWidgetItem(_('Images'), r)
|
||||||
|
|
||||||
self.splitter.setStretchFactor(1, 500)
|
self.splitter.setStretchFactor(1, 500)
|
||||||
try:
|
try:
|
||||||
self.splitter.restoreState(read_state('splitter-state'))
|
self.splitter.restoreState(read_state('splitter-state'))
|
||||||
@ -289,16 +437,20 @@ class ReportsWidget(QWidget):
|
|||||||
|
|
||||||
def __call__(self, data):
|
def __call__(self, data):
|
||||||
self.files(data)
|
self.files(data)
|
||||||
|
self.images(data)
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
save_state('splitter-state', bytearray(self.splitter.saveState()))
|
save_state('splitter-state', bytearray(self.splitter.saveState()))
|
||||||
save_state('report-page', self.reports.currentRow())
|
save_state('report-page', self.reports.currentRow())
|
||||||
self.files.save()
|
self.files.save()
|
||||||
|
self.images.save()
|
||||||
|
|
||||||
class Reports(Dialog):
|
class Reports(Dialog):
|
||||||
|
|
||||||
data_gathered = pyqtSignal(object, object)
|
data_gathered = pyqtSignal(object, object)
|
||||||
edit_requested = pyqtSignal(object)
|
edit_requested = pyqtSignal(object)
|
||||||
|
refresh_starting = pyqtSignal()
|
||||||
|
delete_requested = pyqtSignal(object, object)
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
Dialog.__init__(self, _('Reports'), 'reports-dialog', parent=parent)
|
Dialog.__init__(self, _('Reports'), 'reports-dialog', parent=parent)
|
||||||
@ -312,6 +464,7 @@ class Reports(Dialog):
|
|||||||
l.addWidget(self.bb)
|
l.addWidget(self.bb)
|
||||||
self.reports = r = ReportsWidget(self)
|
self.reports = r = ReportsWidget(self)
|
||||||
r.edit_requested.connect(self.edit_requested)
|
r.edit_requested.connect(self.edit_requested)
|
||||||
|
r.delete_requested.connect(self.confirm_delete)
|
||||||
|
|
||||||
self.pw = pw = QWidget(self)
|
self.pw = pw = QWidget(self)
|
||||||
s.addWidget(pw), s.addWidget(r)
|
s.addWidget(pw), s.addWidget(r)
|
||||||
@ -330,10 +483,18 @@ class Reports(Dialog):
|
|||||||
def sizeHint(self):
|
def sizeHint(self):
|
||||||
return QSize(950, 600)
|
return QSize(950, 600)
|
||||||
|
|
||||||
|
def confirm_delete(self, spine_names, other_names):
|
||||||
|
if not question_dialog(self, _('Are you sure?'), _(
|
||||||
|
'Are you sure you want to delete the selected files?'), det_msg='\n'.join(spine_names | other_names)):
|
||||||
|
return
|
||||||
|
self.delete_requested.emit(spine_names, other_names)
|
||||||
|
QTimer.singleShot(10, self.refresh)
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
self.wait_stack.setCurrentIndex(0)
|
self.wait_stack.setCurrentIndex(0)
|
||||||
self.setCursor(Qt.BusyCursor)
|
self.setCursor(Qt.BusyCursor)
|
||||||
self.pi.startAnimation()
|
self.pi.startAnimation()
|
||||||
|
self.refresh_starting.emit()
|
||||||
t = Thread(name='GatherReportData', target=self.gather_data)
|
t = Thread(name='GatherReportData', target=self.gather_data)
|
||||||
t.daemon = True
|
t.daemon = True
|
||||||
t.start()
|
t.start()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user