From 2697f4d0ec7d0adda6968de76bcfbc600f68f15d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 8 Apr 2011 17:51:07 -0600 Subject: [PATCH 01/20] Fix #755105 (FreeBSD pdftohtml) --- src/calibre/ebooks/pdf/pdftohtml.py | 4 ++- src/calibre/gui2/layout.py | 4 +-- src/calibre/gui2/metadata/single_download.py | 26 +++++++++++++++++--- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/calibre/ebooks/pdf/pdftohtml.py b/src/calibre/ebooks/pdf/pdftohtml.py index 4ac1d0e368..4aa0953738 100644 --- a/src/calibre/ebooks/pdf/pdftohtml.py +++ b/src/calibre/ebooks/pdf/pdftohtml.py @@ -13,7 +13,7 @@ from functools import partial from calibre.ebooks import ConversionError, DRMError from calibre.ptempfile import PersistentTemporaryFile -from calibre import isosx, iswindows, islinux, isfreebsd +from calibre.constants import isosx, iswindows, islinux, isfreebsd from calibre import CurrentDir PDFTOHTML = 'pdftohtml' @@ -43,6 +43,8 @@ def pdftohtml(output_dir, pdf_path, no_images): # This is neccessary as pdftohtml doesn't always (linux) respect absolute paths pdf_path = os.path.abspath(pdf_path) cmd = [PDFTOHTML, '-enc', 'UTF-8', '-noframes', '-p', '-nomerge', '-nodrm', '-q', pdf_path, os.path.basename(index)] + if isfreebsd: + cmd.remove('-nodrm') if no_images: cmd.append('-i') diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index 9a6148eb7b..a7ae479eb4 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -238,7 +238,7 @@ class Spacer(QWidget): # {{{ self.l.addStretch(10) # }}} -class MenuAction(QAction): +class MenuAction(QAction): # {{{ def __init__(self, clone, parent): QAction.__init__(self, clone.text(), parent) @@ -247,7 +247,7 @@ class MenuAction(QAction): def clone_changed(self): self.setText(self.clone.text()) - +# }}} class MenuBar(QMenuBar): # {{{ diff --git a/src/calibre/gui2/metadata/single_download.py b/src/calibre/gui2/metadata/single_download.py index 176c164d3d..7fa052844f 100644 --- a/src/calibre/gui2/metadata/single_download.py +++ b/src/calibre/gui2/metadata/single_download.py @@ -398,12 +398,24 @@ class IdentifyWidget(QWidget): # {{{ self.abort.set() # }}} -class FullFetch(QDialog): # {{{ +class CoverWidget(QWidget): # {{{ def __init__(self, log, parent=None): - QDialog.__init__(self, parent) + QWidget.__init__(self, parent) self.log = log + def start(self, book, current_cover, title, authors): + self.book, self.current_cover = book, current_cover + self.title, self.authors = title, authors + self.log('\n\nStarting cover download for:', book.title) +# }}} + +class FullFetch(QDialog): # {{{ + + def __init__(self, log, current_cover=None, parent=None): + QDialog.__init__(self, parent) + self.log, self.current_cover = log, current_cover + self.setWindowTitle(_('Downloading metadata...')) self.setWindowIcon(QIcon(I('metadata.png'))) @@ -428,12 +440,19 @@ class FullFetch(QDialog): # {{{ self.identify_widget.results_found.connect(self.identify_results_found) self.identify_widget.book_selected.connect(self.book_selected) self.stack.addWidget(self.identify_widget) + + self.cover_widget = CoverWidget(self.log, parent=self) + self.stack.addWidget(self.cover_widget) + self.resize(850, 500) def book_selected(self, book): - print (book) self.next_button.setVisible(False) self.ok_button.setVisible(True) + self.book = book + self.stack.setCurrentIndex(1) + self.cover_widget.start(book, self.current_cover, + self.title, self.authors) def accept(self): # Prevent the usual dialog accept mechanisms from working @@ -453,6 +472,7 @@ class FullFetch(QDialog): # {{{ pass def start(self, title=None, authors=None, identifiers={}): + self.title, self.authors = title, authors self.identify_widget.start(title=title, authors=authors, identifiers=identifiers) self.exec_() From 360bc4df7cf4d68ef8a8b638905b4de9d5a53fa2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 8 Apr 2011 17:55:25 -0600 Subject: [PATCH 02/20] ... --- src/calibre/gui2/preferences/toolbar.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/calibre/gui2/preferences/toolbar.py b/src/calibre/gui2/preferences/toolbar.py index 9512fc7d3d..fc8aed48da 100644 --- a/src/calibre/gui2/preferences/toolbar.py +++ b/src/calibre/gui2/preferences/toolbar.py @@ -80,6 +80,12 @@ class BaseModel(QAbstractListModel): ans.append(n) return ans + def has_action(self, name): + for a in self._data: + if a.name == name: + return True + return False + class AllModel(BaseModel): @@ -291,13 +297,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): def commit(self): # Ensure preferences are showing in either the toolbar or # the menubar. - pref_in_toolbar = lm_in_toolbar = False - cm = self.models['toolbar'] - for x in cm[1]._data: - if x.name == 'Preferences': - pref_in_toolbar = True - if x.name == 'Location Manager': - lm_in_toolbar = True + pref_in_toolbar = self.models['toolbar'][1].has_action('Preferences') + lm_in_toolbar = self.models['toolbar-device'][1].has_action('Location Manager') if not pref_in_toolbar: self.models['menubar'][1].add(['Preferences']) if not lm_in_toolbar: From f7ca790c909478a2d155e58c5585aea3cf760e1f Mon Sep 17 00:00:00 2001 From: John Schember Date: Fri, 8 Apr 2011 20:40:10 -0400 Subject: [PATCH 03/20] Add default menubar and toolbar items for OS X. Prevent preferences from being added to the menubar twice which causes a segfault on OS X. --- src/calibre/gui2/__init__.py | 40 ++++++++++++++++++------- src/calibre/gui2/preferences/toolbar.py | 6 ++-- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 5b6430a5e6..1e81677c30 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -13,7 +13,7 @@ from PyQt4.Qt import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, \ ORG_NAME = 'KovidsBrain' APP_UID = 'libprs500' -from calibre.constants import islinux, iswindows, isfreebsd, isfrozen +from calibre.constants import islinux, iswindows, isfreebsd, isfrozen, isosx from calibre.utils.config import Config, ConfigProxy, dynamic, JSONConfig from calibre.utils.localization import set_qt_translator from calibre.ebooks.metadata.meta import get_metadata, metadata_from_formats @@ -23,25 +23,45 @@ from calibre.utils.date import UNDEFINED_DATE # Setup gprefs {{{ gprefs = JSONConfig('gui') -gprefs.defaults['action-layout-menubar'] = () - -gprefs.defaults['action-layout-menubar-device'] = () - -gprefs.defaults['action-layout-toolbar'] = ( +if isosx: + gprefs.defaults['action-layout-menubar'] = ( + 'Add Books', 'Edit Metadata', 'Convert Books', + 'Choose Library', 'Save To Disk', 'Preferences', + 'Help', + ) + gprefs.defaults['action-layout-menubar-device'] = ( + 'Add Books', 'Edit Metadata', 'Convert Books', + 'Location Manager', 'Location Manager', + 'Send To Device', 'Preferences', 'Help', + ) + gprefs.defaults['action-layout-toolbar'] = ( + 'Add Books', 'Edit Metadata', None, 'Convert Books', 'View', None, + 'Choose Library', 'Donate', None, 'Fetch News', 'Save To Disk', + 'Connect Share', None, 'Remove Books', + ) + gprefs.defaults['action-layout-toolbar-device'] = ( + 'Add Books', 'Edit Metadata', None, 'Convert Books', 'View', + 'Send To Device', None, None, 'Location Manager', None, None, + 'Fetch News', 'Save To Disk', 'Connect Share', None, + 'Remove Books', + ) +else: + gprefs.defaults['action-layout-menubar'] = () + gprefs.defaults['action-layout-menubar-device'] = () + gprefs.defaults['action-layout-toolbar'] = ( 'Add Books', 'Edit Metadata', None, 'Convert Books', 'View', None, 'Choose Library', 'Donate', None, 'Fetch News', 'Save To Disk', 'Connect Share', None, 'Remove Books', None, 'Help', 'Preferences', ) - -gprefs.defaults['action-layout-toolbar-child'] = () - -gprefs.defaults['action-layout-toolbar-device'] = ( + gprefs.defaults['action-layout-toolbar-device'] = ( 'Add Books', 'Edit Metadata', None, 'Convert Books', 'View', 'Send To Device', None, None, 'Location Manager', None, None, 'Fetch News', 'Save To Disk', 'Connect Share', None, 'Remove Books', None, 'Help', 'Preferences', ) +gprefs.defaults['action-layout-toolbar-child'] = () + gprefs.defaults['action-layout-context-menu'] = ( 'Edit Metadata', 'Send To Device', 'Save To Disk', 'Connect Share', 'Copy To Library', None, diff --git a/src/calibre/gui2/preferences/toolbar.py b/src/calibre/gui2/preferences/toolbar.py index fc8aed48da..7f5e0c4441 100644 --- a/src/calibre/gui2/preferences/toolbar.py +++ b/src/calibre/gui2/preferences/toolbar.py @@ -298,10 +298,12 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): # Ensure preferences are showing in either the toolbar or # the menubar. pref_in_toolbar = self.models['toolbar'][1].has_action('Preferences') + pref_in_menubar = self.models['menubar'][1].has_action('Preferences') lm_in_toolbar = self.models['toolbar-device'][1].has_action('Location Manager') - if not pref_in_toolbar: + lm_in_menubar = self.models['menubar-device'][1].has_action('Location Manager') + if not pref_in_toolbar and not pref_in_menubar: self.models['menubar'][1].add(['Preferences']) - if not lm_in_toolbar: + if not lm_in_toolbar and not lm_in_menubar: self.models['menubar-device'][1].add(['Location Manager']) # Save data. From 41074dc97e8f112c7cb79cff2ea1dd61554a74f0 Mon Sep 17 00:00:00 2001 From: John Schember Date: Fri, 8 Apr 2011 21:36:05 -0400 Subject: [PATCH 04/20] Throbbing donate tool button on OS X. --- src/calibre/gui2/layout.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index a7ae479eb4..8a6ef3afe4 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -382,16 +382,12 @@ class ToolBar(QToolBar): # {{{ bar.added_actions.append(ac) bar.setup_tool_button(bar, ac, QToolButton.MenuButtonPopup) elif what == 'Donate': - if isosx: - bar.addAction(self.gui.donate_action) - ch = self.setup_tool_button(bar, self.gui.donate_action) - ch.setText(_('Donate')) - else: - self.d_widget = QWidget() - self.d_widget.setLayout(QVBoxLayout()) - self.d_widget.layout().addWidget(self.donate_button) - bar.addWidget(self.d_widget) - self.showing_donate = True + self.d_widget = QWidget() + self.d_widget.setLayout(QVBoxLayout()) + self.d_widget.layout().addWidget(self.donate_button) + self.d_widget.setStyleSheet('QWidget, QToolButton {background-color: none; border: none; }') + bar.addWidget(self.d_widget) + self.showing_donate = True elif what in self.gui.iactions: action = self.gui.iactions[what] bar.addAction(action.qaction) From bb46029eb622b814f687a7940f36c978ba15b6e3 Mon Sep 17 00:00:00 2001 From: John Schember Date: Fri, 8 Apr 2011 21:44:28 -0400 Subject: [PATCH 05/20] ... --- src/calibre/gui2/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 1e81677c30..ddef5e1265 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -31,8 +31,8 @@ if isosx: ) gprefs.defaults['action-layout-menubar-device'] = ( 'Add Books', 'Edit Metadata', 'Convert Books', - 'Location Manager', 'Location Manager', - 'Send To Device', 'Preferences', 'Help', + 'Location Manager', 'Send To Device', + 'Save To Disk', 'Preferences', 'Help', ) gprefs.defaults['action-layout-toolbar'] = ( 'Add Books', 'Edit Metadata', None, 'Convert Books', 'View', None, From 2524105c84c708efa70afc0f266381b79715fcfd Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 9 Apr 2011 09:59:47 +0100 Subject: [PATCH 06/20] 1) Move bool_are_tristate and edit_metadata_layout preferences to behavior from columns 2) Move edit_metadata_layout to gprefs --- src/calibre/gui2/__init__.py | 1 + src/calibre/gui2/metadata/single.py | 2 +- src/calibre/gui2/preferences/behavior.py | 11 +- src/calibre/gui2/preferences/behavior.ui | 157 +++++++++++++++-------- src/calibre/gui2/preferences/columns.py | 13 -- src/calibre/gui2/preferences/columns.ui | 61 --------- src/calibre/library/database2.py | 4 +- 7 files changed, 120 insertions(+), 129 deletions(-) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index ddef5e1265..22aaabf592 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -81,6 +81,7 @@ gprefs.defaults['toolbar_text'] = 'auto' gprefs.defaults['font'] = None gprefs.defaults['tags_browser_partition_method'] = 'first letter' gprefs.defaults['tags_browser_collapse_at'] = 100 +gprefs.defaults['edit_metadata_single_layout'] = 'default' # }}} diff --git a/src/calibre/gui2/metadata/single.py b/src/calibre/gui2/metadata/single.py index cba877b249..2ef183fca5 100644 --- a/src/calibre/gui2/metadata/single.py +++ b/src/calibre/gui2/metadata/single.py @@ -658,7 +658,7 @@ editors = {'default': MetadataSingleDialog, 'alt1': MetadataSingleDialogAlt1} def edit_metadata(db, row_list, current_row, parent=None, view_slot=None, set_current_callback=None): - cls = db.prefs.get('edit_metadata_single_layout', '') + cls = gprefs.get('edit_metadata_single_layout', '') if cls not in editors: cls = 'default' d = editors[cls](db, parent) diff --git a/src/calibre/gui2/preferences/behavior.py b/src/calibre/gui2/preferences/behavior.py index 342b5197c9..d64af343f2 100644 --- a/src/calibre/gui2/preferences/behavior.py +++ b/src/calibre/gui2/preferences/behavior.py @@ -11,7 +11,7 @@ from PyQt4.Qt import Qt, QVariant, QListWidgetItem from calibre.gui2.preferences import ConfigWidgetBase, test_widget, Setting from calibre.gui2.preferences.behavior_ui import Ui_Form -from calibre.gui2 import config, info_dialog, dynamic +from calibre.gui2 import config, info_dialog, dynamic, gprefs from calibre.utils.config import prefs from calibre.customize.ui import available_output_formats, all_input_formats from calibre.utils.search_query_parser import saved_searches @@ -19,6 +19,7 @@ from calibre.ebooks import BOOK_EXTENSIONS from calibre.ebooks.oeb.iterator import is_supported from calibre.constants import iswindows from calibre.utils.icu import sort_key +from calibre.utils.config import test_eight_code class OutputFormatSetting(Setting): @@ -62,6 +63,14 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): signal = getattr(self.opt_internally_viewed_formats, 'item'+signal) signal.connect(self.internally_viewed_formats_changed) + r('bools_are_tristate', db.prefs, restart_required=True) + if test_eight_code: + r = self.register + choices = [(_('Default'), 'default'), (_('Compact Metadata'), 'alt1')] + r('edit_metadata_single_layout', gprefs, choices=choices) + else: + self.opt_edit_metadata_single_layout.setVisible(False) + self.edit_metadata_single_label.setVisible(False) def initialize(self): ConfigWidgetBase.initialize(self) diff --git a/src/calibre/gui2/preferences/behavior.ui b/src/calibre/gui2/preferences/behavior.ui index 0f35d28cd5..12ff18b58a 100644 --- a/src/calibre/gui2/preferences/behavior.ui +++ b/src/calibre/gui2/preferences/behavior.ui @@ -14,44 +14,92 @@ Form - + + + + Qt::Horizontal + + + + 10 + 00 + + + + + &Overwrite author and title by default when fetching metadata - + Download &social metadata (tags/ratings/etc.) by default - + Show notification when &new version is available - + + + + Custom Yes/No columns have three values (Requires restart): + + + If checked, Yes/No custom columns values can be Yes, No, or Unknown. +If not checked, the values can be Yes or No. + + + + Automatically send downloaded &news to ebook reader - + &Delete news from library when it is automatically sent to reader - - - + + + + + + Preferred &output format: + + + opt_output_format + + + + + + + QComboBox::AdjustToMinimumContentsLengthWithIcon + + + 10 + + + + + + + + Default network &timeout: @@ -61,7 +109,7 @@ - + Set the default timeout for network fetches (i.e. anytime we go out to the internet to get information) @@ -80,7 +128,21 @@ - + + + + + + + + Job &priority: + + + opt_worker_process_priority + + + + QComboBox::AdjustToMinimumContentsLengthWithIcon @@ -105,37 +167,11 @@ - - - - Job &priority: - - - opt_worker_process_priority - - - - - - - Preferred &output format: - - - opt_output_format - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - 10 - - - - + + + + + Restriction to apply when the current library is opened: @@ -145,7 +181,7 @@ - + @@ -166,14 +202,28 @@ - - - - Reset all disabled &confirmation dialogs - - + + + + + + Edit metadata (single) layout: + + + opt_edit_metadata_single_layout + + + + + + + Choose a different layout for the Edit Metadata dialog. The compact metadata layout favors editing custom metadata over changing covers and formats. + + + + - + Preferred &input format order: @@ -235,7 +285,7 @@ - + Use internal &viewer for: @@ -254,6 +304,13 @@ + + + + Reset all disabled &confirmation dialogs + + + diff --git a/src/calibre/gui2/preferences/columns.py b/src/calibre/gui2/preferences/columns.py index 4079e2d3f4..03a50e6f3a 100644 --- a/src/calibre/gui2/preferences/columns.py +++ b/src/calibre/gui2/preferences/columns.py @@ -13,7 +13,6 @@ from calibre.gui2.preferences import ConfigWidgetBase, test_widget from calibre.gui2.preferences.columns_ui import Ui_Form from calibre.gui2.preferences.create_custom_column import CreateCustomColumn from calibre.gui2 import error_dialog, question_dialog, ALL_COLUMNS -from calibre.utils.config import test_eight_code class ConfigWidget(ConfigWidgetBase, Ui_Form): @@ -34,14 +33,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): signal = getattr(self.opt_columns, 'item'+signal) signal.connect(self.columns_changed) - if test_eight_code: - r = self.register - choices = [(_('Default'), 'default'), (_('Compact Metadata'), 'alt1')] - r('edit_metadata_single_layout', db.prefs, choices=choices) - r('bools_are_tristate', db.prefs, restart_required=True) - else: - self.items_in_v_eight.setVisible(False) - def initialize(self): ConfigWidgetBase.initialize(self) self.init_columns() @@ -178,10 +169,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): must_restart = True return must_restart - def refresh_gui(self, gui): - gui.library_view.reset() - - if __name__ == '__main__': from PyQt4.Qt import QApplication diff --git a/src/calibre/gui2/preferences/columns.ui b/src/calibre/gui2/preferences/columns.ui index 48944b1c04..a9d82530ec 100644 --- a/src/calibre/gui2/preferences/columns.ui +++ b/src/calibre/gui2/preferences/columns.ui @@ -197,67 +197,6 @@ - - - - - - Related Options - - - - - - Edit metadata layout: - - - opt_edit_metadata_single_layout - - - - - - - Choose a different layout for the Edit Metadata dialog. Alternate layouts make it easier to edit custom columns. - - - - - - - Boolean columns are tristate: - - - opt_bools_are_tristate - - - - - - - If checked, boolean columns values can be Yes, No, and Unknown. -If not checked, the values can be Yes and No. - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index ccc5b60427..b05e029c38 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -40,7 +40,6 @@ from calibre.ebooks import BOOK_EXTENSIONS, check_ebook_format from calibre.utils.magick.draw import save_cover_data_to from calibre.utils.recycle_bin import delete_file, delete_tree from calibre.utils.formatter_functions import load_user_template_functions -from calibre.utils.config import test_eight_code copyfile = os.link if hasattr(os, 'link') else shutil.copyfile @@ -213,11 +212,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): defs = self.prefs.defaults defs['gui_restriction'] = defs['cs_restriction'] = '' defs['categories_using_hierarchy'] = [] - defs['edit_metadata_single_layout'] = 'default' defs['bools_are_tristate'] = \ tweaks.get('bool_custom_columns_are_tristate', 'yes') == 'yes' - if self.prefs.get('bools_are_tristate') is None or not test_eight_code: + if self.prefs.get('bools_are_tristate') is None: self.prefs.set('bools_are_tristate', defs['bools_are_tristate']) # Migrate saved search and user categories to db preference scheme From d68d25c1067827c9d6d7f09eda3b47ebf0f2bcda Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 9 Apr 2011 11:49:18 +0100 Subject: [PATCH 07/20] Remove bool tristate tweak, now that it is in preferences -> behavior. --- resources/default_tweaks.py | 7 ------- src/calibre/library/database2.py | 1 + 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py index 1cf699efa3..c4c951f980 100644 --- a/resources/default_tweaks.py +++ b/resources/default_tweaks.py @@ -88,13 +88,6 @@ categories_collapsed_rating_template = r'{first.avg_rating:4.2f:ifempty(0)} - {l categories_collapsed_popularity_template = r'{first.count:d} - {last.count:d}' -#: Set boolean custom columns to be tristate -# Set whether boolean custom columns are two- or three-valued. -# Two-values for true booleans -# three-values for yes/no/unknown -# Set to 'yes' for three-values, 'no' for two-values -bool_custom_columns_are_tristate = 'yes' - #: Specify columns to sort the booklist by on startup # Provide a set of columns to be sorted on when calibre starts # The argument is None if saved sort history is to be used diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index b05e029c38..50ecc4f1e5 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -213,6 +213,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): defs['gui_restriction'] = defs['cs_restriction'] = '' defs['categories_using_hierarchy'] = [] + # Migrate the bool tristate tweak defs['bools_are_tristate'] = \ tweaks.get('bool_custom_columns_are_tristate', 'yes') == 'yes' if self.prefs.get('bools_are_tristate') is None: From 2577a155c11e0a688f8fbf63c85b9c7a87059391 Mon Sep 17 00:00:00 2001 From: John Schember Date: Sat, 9 Apr 2011 10:04:00 -0400 Subject: [PATCH 08/20] Apply styling to child toolbar. --- src/calibre/gui2/layout.py | 52 +++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index 8a6ef3afe4..54f97d1039 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -308,22 +308,44 @@ class MenuBar(QMenuBar): # {{{ ac.setMenu(m) return ac - - # }}} -class ToolBar(QToolBar): # {{{ +class BaseToolBar(QToolBar): # {{{ - def __init__(self, donate, location_manager, child_bar, parent): + def __init__(self, parent): QToolBar.__init__(self, parent) - self.gui = parent - self.child_bar = child_bar self.setContextMenuPolicy(Qt.PreventContextMenu) self.setMovable(False) self.setFloatable(False) self.setOrientation(Qt.Horizontal) self.setAllowedAreas(Qt.TopToolBarArea|Qt.BottomToolBarArea) self.setStyleSheet('QToolButton:checked { font-weight: bold }') + self.preferred_width = self.sizeHint().width() + + def resizeEvent(self, ev): + QToolBar.resizeEvent(self, ev) + style = self.get_text_style() + self.setToolButtonStyle(style) + + def get_text_style(self): + style = Qt.ToolButtonTextUnderIcon + s = gprefs['toolbar_icon_size'] + if s != 'off': + p = gprefs['toolbar_text'] + if p == 'never': + style = Qt.ToolButtonIconOnly + elif p == 'auto' and self.preferred_width > self.width()+35: + style = Qt.ToolButtonIconOnly + return style + +# }}} + +class ToolBar(BaseToolBar): # {{{ + + def __init__(self, donate, location_manager, child_bar, parent): + BaseToolBar.__init__(self, parent) + self.gui = parent + self.child_bar = child_bar self.donate_button = donate self.apply_settings() @@ -333,7 +355,6 @@ class ToolBar(QToolBar): # {{{ donate.setCursor(Qt.PointingHandCursor) self.added_actions = [] self.build_bar() - self.preferred_width = self.sizeHint().width() self.setAcceptDrops(True) def apply_settings(self): @@ -404,21 +425,6 @@ class ToolBar(QToolBar): # {{{ ch.setPopupMode(menu_mode) return ch - def resizeEvent(self, ev): - QToolBar.resizeEvent(self, ev) - style = Qt.ToolButtonTextUnderIcon - s = gprefs['toolbar_icon_size'] - if s != 'off': - p = gprefs['toolbar_text'] - if p == 'never': - style = Qt.ToolButtonIconOnly - - if p == 'auto' and self.preferred_width > self.width()+35 and \ - not gprefs['action-layout-toolbar-child']: - style = Qt.ToolButtonIconOnly - - self.setToolButtonStyle(style) - def database_changed(self, db): pass @@ -496,7 +502,7 @@ class MainWindowMixin(object): # {{{ self.iactions['Fetch News'].init_scheduler(db) self.search_bar = SearchBar(self) - self.child_bar = QToolBar(self) + self.child_bar = BaseToolBar(self) self.tool_bar = ToolBar(self.donate_button, self.location_manager, self.child_bar, self) self.addToolBar(Qt.TopToolBarArea, self.tool_bar) From 173a7dfc317337bd9751007c0524bb1cd507bc59 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Apr 2011 08:37:56 -0600 Subject: [PATCH 09/20] Fix #755444 (Connect/share icon to show content server running) --- ...share_on.png (PNG Image, 127x126 pixels).png | Bin 0 -> 11617 bytes src/calibre/gui2/actions/device.py | 4 ++++ 2 files changed, 4 insertions(+) create mode 100644 resources/images/connect_share_on.png (PNG Image, 127x126 pixels).png diff --git a/resources/images/connect_share_on.png (PNG Image, 127x126 pixels).png b/resources/images/connect_share_on.png (PNG Image, 127x126 pixels).png new file mode 100644 index 0000000000000000000000000000000000000000..3d431be1a1eebaac758bf5c9c3df4bc5e3dadf7f GIT binary patch literal 11617 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 literal 0 HcmV?d00001 diff --git a/src/calibre/gui2/actions/device.py b/src/calibre/gui2/actions/device.py index 64bc4e69d7..debcbb6c1a 100644 --- a/src/calibre/gui2/actions/device.py +++ b/src/calibre/gui2/actions/device.py @@ -165,6 +165,10 @@ class ConnectShareAction(InterfaceAction): def content_server_state_changed(self, running): self.share_conn_menu.server_state_changed(running) + if running: + self.qaction.setIcon(QIcon(I('connect_share_on.png'))) + else: + self.qaction.setIcon(QIcon(I('connect_share.png'))) def toggle_content_server(self): if self.gui.content_server is None: From 745db1feb0d9212b70e780aad997c459ebb9203c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Apr 2011 08:55:04 -0600 Subject: [PATCH 10/20] Dvhn by Reijndert --- recipes/dvhn.recipe | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 recipes/dvhn.recipe diff --git a/recipes/dvhn.recipe b/recipes/dvhn.recipe new file mode 100644 index 0000000000..4c093aa9d2 --- /dev/null +++ b/recipes/dvhn.recipe @@ -0,0 +1,32 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class AdvancedUserRecipe1302341394(BasicNewsRecipe): + title = u'DvhN' + oldest_article = 1 + max_articles_per_feed = 200 + + __author__ = 'Reijndert' + no_stylesheets = True + cover_url = 'http://www.dvhn.nl/template/Dagblad_v2.0/gfx/logo_DvhN.gif' + language = 'nl' + country = 'NL' + version = 1 + publisher = u'Dagblad van het Noorden' + category = u'Nieuws' + description = u'Nieuws uit Noord Nederland' + + + keep_only_tags = [dict(name='div', attrs={'id':'fullPicture'}) + ,dict(name='div', attrs={'id':'articleText'}) + ] + + remove_tags = [ + dict(name=['object','link','iframe','base']) + ,dict(name='span',attrs={'class':'copyright'}) + ] + + feeds = [(u'Drenthe', u'http://www.dvhn.nl/nieuws/drenthe/index.jsp?service=rss'), (u'Groningen', u'http://www.dvhn.nl/nieuws/groningen/index.jsp?service=rss'), (u'Nederland', u'http://www.dvhn.nl/nieuws/nederland/index.jsp?service=rss'), (u'Wereld', u'http://www.dvhn.nl/nieuws/wereld/index.jsp?service=rss'), (u'Economie', u'http://www.dvhn.nl/nieuws/economie/index.jsp?service=rss'), (u'Sport', u'http://www.dvhn.nl/nieuws/sport/index.jsp?service=rss'), (u'Cultuur', u'http://www.dvhn.nl/nieuws/kunst/index.jsp?service=rss'), (u'24 Uur', u'http://www.dvhn.nl/nieuws/24uurdvhn/index.jsp?service=rss&selectiontype=last24hours')] + + extra_css = ''' + body {font-family: verdana, arial, helvetica, geneva, sans-serif;} + ''' From 394cdedce3889b88c258fcb6ad477fb3a66bf2a2 Mon Sep 17 00:00:00 2001 From: John Schember Date: Sat, 9 Apr 2011 11:46:08 -0400 Subject: [PATCH 11/20] Calculate preferred with with a built toolbar --- src/calibre/gui2/layout.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index 54f97d1039..092f971862 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -320,7 +320,6 @@ class BaseToolBar(QToolBar): # {{{ self.setOrientation(Qt.Horizontal) self.setAllowedAreas(Qt.TopToolBarArea|Qt.BottomToolBarArea) self.setStyleSheet('QToolButton:checked { font-weight: bold }') - self.preferred_width = self.sizeHint().width() def resizeEvent(self, ev): QToolBar.resizeEvent(self, ev) @@ -334,7 +333,7 @@ class BaseToolBar(QToolBar): # {{{ p = gprefs['toolbar_text'] if p == 'never': style = Qt.ToolButtonIconOnly - elif p == 'auto' and self.preferred_width > self.width()+35: + elif p == 'auto' and self.sizeHint().width() > self.width()+35: style = Qt.ToolButtonIconOnly return style From b7b156b2484e95e3047795912bd28b6da9ca5467 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Apr 2011 10:09:55 -0600 Subject: [PATCH 12/20] ... --- ...ge, 127x126 pixels).png => connect_share_on.png} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename resources/images/{connect_share_on.png (PNG Image, 127x126 pixels).png => connect_share_on.png} (100%) diff --git a/resources/images/connect_share_on.png (PNG Image, 127x126 pixels).png b/resources/images/connect_share_on.png similarity index 100% rename from resources/images/connect_share_on.png (PNG Image, 127x126 pixels).png rename to resources/images/connect_share_on.png From bc038e1b7a842cef16d92784b1de2b3238587e94 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Apr 2011 10:25:08 -0600 Subject: [PATCH 13/20] ... --- src/calibre/gui2/metadata/single_download.py | 76 +++++++++++++++++++- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/src/calibre/gui2/metadata/single_download.py b/src/calibre/gui2/metadata/single_download.py index 7fa052844f..b6bd38a828 100644 --- a/src/calibre/gui2/metadata/single_download.py +++ b/src/calibre/gui2/metadata/single_download.py @@ -13,7 +13,8 @@ from operator import attrgetter from PyQt4.Qt import (QStyledItemDelegate, QTextDocument, QRectF, QIcon, Qt, QStyle, QApplication, QDialog, QVBoxLayout, QLabel, QDialogButtonBox, QStackedWidget, QWidget, QTableView, QGridLayout, QFontInfo, QPalette, - QTimer, pyqtSignal, QAbstractTableModel, QVariant, QSize) + QTimer, pyqtSignal, QAbstractTableModel, QVariant, QSize, QListView, + QPixmap, QAbstractListModel) from PyQt4.QtWebKit import QWebView from calibre.customize.ui import metadata_plugins @@ -398,16 +399,85 @@ class IdentifyWidget(QWidget): # {{{ self.abort.set() # }}} +class CoversModel(QAbstractListModel): # {{{ + + def __init__(self, log, current_cover, parent=None): + QAbstractListModel.__init__(self, parent) + + if current_cover is None: + current_cover = QPixmap(I('default_cover.png')) + + self.covers = [self.get_item(_('Current cover'), current_cover)] + for i in range(10): + self.covers.append(self.covers[0]) + self.log = log + + def get_item(self, src, pmap): + sz = '%dx%d'%(pmap.width(), pmap.height()) + text = QVariant(src + '\n' + sz) + scaled = pmap.scaled(150, 200, Qt.IgnoreAspectRatio, + Qt.SmoothTransformation) + return (text, QVariant(scaled), pmap) + + def rowCount(self, parent=None): + return len(self.covers) + + def data(self, index, role): + try: + text, pmap = self.covers[index.row()][:2] + except: + return None + if role == Qt.DecorationRole: + return pmap + if role == Qt.DisplayRole: + return text + return NONE +# }}} + +class CoversView(QListView): # {{{ + + def __init__(self, log, current_cover, parent=None): + QListView.__init__(self, parent) + self.m = CoversModel(log, current_cover, self) + self.setModel(self.m) + + self.setFlow(self.LeftToRight) + self.setWrapping(True) + self.setResizeMode(self.Adjust) + self.setGridSize(QSize(190, 260)) + self.setIconSize(QSize(150, 200)) + self.setSelectionMode(self.SingleSelection) + self.setViewMode(self.IconMode) + + def select(self, num): + current = self.model().index(num) + sm = self.selectionModel() + sm.select(current, sm.SelectCurrent) + +# }}} + class CoverWidget(QWidget): # {{{ - def __init__(self, log, parent=None): + def __init__(self, log, current_cover, parent=None): QWidget.__init__(self, parent) self.log = log + self.l = l = QGridLayout() + self.setLayout(l) + + self.msg = QLabel() + self.msg.setWordWrap(True) + l.addWidget(self.msg, 0, 0) + + self.covers_view = CoversView(log, current_cover, self) + l.addWidget(self.covers_view, 1, 0) + def start(self, book, current_cover, title, authors): self.book, self.current_cover = book, current_cover self.title, self.authors = title, authors self.log('\n\nStarting cover download for:', book.title) + self.msg.setText(_('Downloading covers for %s, please wait...')%book.title) + self.covers_view.select(0) # }}} class FullFetch(QDialog): # {{{ @@ -441,7 +511,7 @@ class FullFetch(QDialog): # {{{ self.identify_widget.book_selected.connect(self.book_selected) self.stack.addWidget(self.identify_widget) - self.cover_widget = CoverWidget(self.log, parent=self) + self.cover_widget = CoverWidget(self.log, self.current_cover, parent=self) self.stack.addWidget(self.cover_widget) self.resize(850, 500) From f8d4e6bfa055a35a8dfd3eb742c16246d97b6b8c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Apr 2011 10:40:56 -0600 Subject: [PATCH 14/20] Fix segfault caused by calling sizeHint from within a resize event handler --- src/calibre/gui2/layout.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index c90c1312ae..e98817a02f 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -320,6 +320,7 @@ class BaseToolBar(QToolBar): # {{{ self.setOrientation(Qt.Horizontal) self.setAllowedAreas(Qt.TopToolBarArea|Qt.BottomToolBarArea) self.setStyleSheet('QToolButton:checked { font-weight: bold }') + self.preferred_width = self.sizeHint().width() def resizeEvent(self, ev): QToolBar.resizeEvent(self, ev) @@ -333,7 +334,7 @@ class BaseToolBar(QToolBar): # {{{ p = gprefs['toolbar_text'] if p == 'never': style = Qt.ToolButtonIconOnly - elif p == 'auto' and self.sizeHint().width() > self.width()+35: + elif p == 'auto' and self.preferred_width > self.width()+35: style = Qt.ToolButtonIconOnly return style @@ -414,6 +415,8 @@ class ToolBar(BaseToolBar): # {{{ bar.addAction(action.qaction) self.added_actions.append(action.qaction) self.setup_tool_button(bar, action.qaction, action.popup_type) + self.preferred_width = self.sizeHint().width() + self.child_bar.preferred_width = self.child_bar.sizeHint().width() def setup_tool_button(self, bar, ac, menu_mode=None): ch = bar.widgetForAction(ac) From b231742a012a1b5a1e9ba30b4564a482b2d85018 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Apr 2011 11:35:39 -0600 Subject: [PATCH 15/20] Animated list views, what fun --- resources/images/spinner.gif | Bin 0 -> 10848 bytes src/calibre/ebooks/metadata/sources/amazon.py | 2 +- src/calibre/gui2/metadata/single_download.py | 65 +++++++++++++++--- src/calibre/utils/config.py | 3 +- 4 files changed, 56 insertions(+), 14 deletions(-) create mode 100644 resources/images/spinner.gif diff --git a/resources/images/spinner.gif b/resources/images/spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..5e864f2e37d51e2b45731822225161780027ea2e GIT binary patch literal 10848 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 literal 0 HcmV?d00001 diff --git a/src/calibre/ebooks/metadata/sources/amazon.py b/src/calibre/ebooks/metadata/sources/amazon.py index b070132de9..4722873f77 100644 --- a/src/calibre/ebooks/metadata/sources/amazon.py +++ b/src/calibre/ebooks/metadata/sources/amazon.py @@ -279,7 +279,7 @@ class Worker(Thread): # Get details {{{ class Amazon(Source): - name = 'Amazon Store' + name = 'Amazon Web' description = _('Downloads metadata from Amazon') capabilities = frozenset(['identify', 'cover']) diff --git a/src/calibre/gui2/metadata/single_download.py b/src/calibre/gui2/metadata/single_download.py index b6bd38a828..2c9b234fba 100644 --- a/src/calibre/gui2/metadata/single_download.py +++ b/src/calibre/gui2/metadata/single_download.py @@ -14,7 +14,7 @@ from PyQt4.Qt import (QStyledItemDelegate, QTextDocument, QRectF, QIcon, Qt, QStyle, QApplication, QDialog, QVBoxLayout, QLabel, QDialogButtonBox, QStackedWidget, QWidget, QTableView, QGridLayout, QFontInfo, QPalette, QTimer, pyqtSignal, QAbstractTableModel, QVariant, QSize, QListView, - QPixmap, QAbstractListModel) + QPixmap, QAbstractListModel, QMovie) from PyQt4.QtWebKit import QWebView from calibre.customize.ui import metadata_plugins @@ -407,33 +407,66 @@ class CoversModel(QAbstractListModel): # {{{ if current_cover is None: current_cover = QPixmap(I('default_cover.png')) - self.covers = [self.get_item(_('Current cover'), current_cover)] - for i in range(10): - self.covers.append(self.covers[0]) + self.blank = QPixmap(I('blank.png')).scaled(150, 200) + + self.covers = [self.get_item(_('Current cover'), current_cover, False)] + for plugin in metadata_plugins(['cover']): + self.covers.append((plugin.name+'\n'+_('Searching...'), + QVariant(self.blank), None, True)) self.log = log - def get_item(self, src, pmap): + def get_item(self, src, pmap, waiting=True): sz = '%dx%d'%(pmap.width(), pmap.height()) text = QVariant(src + '\n' + sz) scaled = pmap.scaled(150, 200, Qt.IgnoreAspectRatio, Qt.SmoothTransformation) - return (text, QVariant(scaled), pmap) + return (text, QVariant(scaled), pmap, waiting) def rowCount(self, parent=None): return len(self.covers) def data(self, index, role): try: - text, pmap = self.covers[index.row()][:2] + text, pmap, cover, waiting = self.covers[index.row()] except: - return None + return NONE if role == Qt.DecorationRole: return pmap if role == Qt.DisplayRole: return text + if role == Qt.UserRole: + return waiting return NONE # }}} +class CoverDelegate(QStyledItemDelegate): + + needs_redraw = pyqtSignal() + + def __init__(self, parent=None): + QStyledItemDelegate.__init__(self, parent) + + self.movie = QMovie(I('spinner.gif')) + self.movie.frameChanged.connect(self.frame_changed) + + def frame_changed(self, *args): + self.needs_redraw.emit() + + def start_movie(self): + self.movie.start() + + def stop_movie(self): + self.movie.stop() + + def paint(self, painter, option, index): + waiting = index.data(Qt.UserRole).toBool() + if waiting: + pixmap = self.movie.currentPixmap() + prect = pixmap.rect() + prect.moveCenter(option.rect.center()) + painter.drawPixmap(prect, pixmap, pixmap.rect()) + QStyledItemDelegate.paint(self, painter, option, index) + class CoversView(QListView): # {{{ def __init__(self, log, current_cover, parent=None): @@ -449,11 +482,20 @@ class CoversView(QListView): # {{{ self.setSelectionMode(self.SingleSelection) self.setViewMode(self.IconMode) + self.delegate = CoverDelegate(self) + self.setItemDelegate(self.delegate) + self.delegate.needs_redraw.connect(self.viewport().update, + type=Qt.QueuedConnection) + def select(self, num): current = self.model().index(num) sm = self.selectionModel() sm.select(current, sm.SelectCurrent) + def start(self): + self.select(0) + self.delegate.start_movie() + # }}} class CoverWidget(QWidget): # {{{ @@ -476,8 +518,9 @@ class CoverWidget(QWidget): # {{{ self.book, self.current_cover = book, current_cover self.title, self.authors = title, authors self.log('\n\nStarting cover download for:', book.title) - self.msg.setText(_('Downloading covers for %s, please wait...')%book.title) - self.covers_view.select(0) + self.msg.setText('

'+_('Downloading covers for %s, please wait...')%book.title) + self.covers_view.start() + # }}} class FullFetch(QDialog): # {{{ @@ -514,7 +557,7 @@ class FullFetch(QDialog): # {{{ self.cover_widget = CoverWidget(self.log, self.current_cover, parent=self) self.stack.addWidget(self.cover_widget) - self.resize(850, 500) + self.resize(850, 550) def book_selected(self, book): self.next_button.setVisible(False) diff --git a/src/calibre/utils/config.py b/src/calibre/utils/config.py index 078971428d..7f2c75a272 100644 --- a/src/calibre/utils/config.py +++ b/src/calibre/utils/config.py @@ -786,8 +786,7 @@ def write_tweaks(raw): tweaks = read_tweaks() test_eight_code = tweaks.get('test_eight_code', False) # test_eight_code notes -# Change documentation of bool columns are tristate to indicate that it can be -# overridden on a per library basis via Preferences->Custom columns +# Change Amazon plugin name to just Amazon def migrate(): if hasattr(os, 'geteuid') and os.geteuid() == 0: From 3de4c6f7ca847d1e162c0e4a4eafb7491750affd Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Apr 2011 12:43:18 -0600 Subject: [PATCH 16/20] New usb ids for the entourage edge --- src/calibre/devices/edge/driver.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/calibre/devices/edge/driver.py b/src/calibre/devices/edge/driver.py index d14763f313..9491b9bc68 100644 --- a/src/calibre/devices/edge/driver.py +++ b/src/calibre/devices/edge/driver.py @@ -26,9 +26,9 @@ class EDGE(USBMS): PRODUCT_ID = [0x0c02] BCD = [0x0223] - VENDOR_NAME = 'ANDROID' - WINDOWS_MAIN_MEM = '__FILE-STOR_GADG' - WINDOWS_CARD_A_MEM = '__FILE-STOR_GADG' + VENDOR_NAME = ['ANDROID', 'LINUX'] + WINDOWS_MAIN_MEM = ['__FILE-STOR_GADG', 'FILE-CD_GADGET'] + WINDOWS_CARD_A_MEM = ['__FILE-STOR_GADG', 'FILE-CD_GADGET'] MAIN_MEMORY_VOLUME_LABEL = 'Edge Main Memory' STORAGE_CARD_VOLUME_LABEL = 'Edge Storage Card' From d2fe0bf7ee71c8b4660f36aa01cf4113b8a7e33c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Apr 2011 15:41:24 -0600 Subject: [PATCH 17/20] Directly draw the spinner instead of using an animated GIF --- resources/images/spinner.gif | Bin 10848 -> 0 bytes src/calibre/gui2/metadata/single_download.py | 256 ++++++++++++++++--- 2 files changed, 225 insertions(+), 31 deletions(-) delete mode 100644 resources/images/spinner.gif diff --git a/resources/images/spinner.gif b/resources/images/spinner.gif deleted file mode 100644 index 5e864f2e37d51e2b45731822225161780027ea2e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10848 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 diff --git a/src/calibre/gui2/metadata/single_download.py b/src/calibre/gui2/metadata/single_download.py index 2c9b234fba..c397a8660a 100644 --- a/src/calibre/gui2/metadata/single_download.py +++ b/src/calibre/gui2/metadata/single_download.py @@ -9,12 +9,13 @@ __docformat__ = 'restructuredtext en' from threading import Thread, Event from operator import attrgetter +from Queue import Queue, Empty from PyQt4.Qt import (QStyledItemDelegate, QTextDocument, QRectF, QIcon, Qt, QStyle, QApplication, QDialog, QVBoxLayout, QLabel, QDialogButtonBox, QStackedWidget, QWidget, QTableView, QGridLayout, QFontInfo, QPalette, QTimer, pyqtSignal, QAbstractTableModel, QVariant, QSize, QListView, - QPixmap, QAbstractListModel, QMovie) + QPixmap, QAbstractListModel, QColor, QRect) from PyQt4.QtWebKit import QWebView from calibre.customize.ui import metadata_plugins @@ -25,6 +26,9 @@ from calibre.ebooks.metadata.book.base import Metadata from calibre.gui2 import error_dialog, NONE from calibre.utils.date import utcnow, fromordinal, format_date from calibre.library.comments import comments_to_html +from calibre import force_unicode + +DEVELOP_DIALOG = False class RichTextDelegate(QStyledItemDelegate): # {{{ @@ -269,7 +273,7 @@ class IdentifyWorker(Thread): # {{{ def run(self): try: - if True: + if DEVELOP_DIALOG: self.results = self.sample_results() else: self.results = identify(self.log, self.abort, title=self.title, @@ -278,7 +282,7 @@ class IdentifyWorker(Thread): # {{{ result.gui_rank = i except: import traceback - self.error = traceback.format_exc() + self.error = force_unicode(traceback.format_exc()) # }}} class IdentifyWidget(QWidget): # {{{ @@ -399,9 +403,39 @@ class IdentifyWidget(QWidget): # {{{ self.abort.set() # }}} +class CoverWorker(Thread): # {{{ + + def __init__(self, log, abort, title, authors, identifiers): + Thread.__init__(self) + self.daemon = True + + self.log, self.abort = log, abort + self.title, self.authors, self.identifiers = (title, authors, + identifiers) + + self.rq = Queue() + self.error = None + + def fake_run(self): + import time + time.sleep(2) + + def run(self): + try: + if DEVELOP_DIALOG: + self.fake_run() + else: + from calibre.ebooks.metadata.sources.covers import run_download + run_download(self.log, self.rq, self.abort, title=self.title, + authors=self.authors, identifiers=self.identifiers) + except: + import traceback + self.error = force_unicode(traceback.format_exc()) +# }}} + class CoversModel(QAbstractListModel): # {{{ - def __init__(self, log, current_cover, parent=None): + def __init__(self, current_cover, parent=None): QAbstractListModel.__init__(self, parent) if current_cover is None: @@ -409,13 +443,14 @@ class CoversModel(QAbstractListModel): # {{{ self.blank = QPixmap(I('blank.png')).scaled(150, 200) - self.covers = [self.get_item(_('Current cover'), current_cover, False)] - for plugin in metadata_plugins(['cover']): + self.covers = [self.get_item(_('Current cover'), current_cover)] + self.plugin_map = {} + for i, plugin in enumerate(metadata_plugins(['cover'])): self.covers.append((plugin.name+'\n'+_('Searching...'), QVariant(self.blank), None, True)) - self.log = log + self.plugin_map[plugin] = i+1 - def get_item(self, src, pmap, waiting=True): + def get_item(self, src, pmap, waiting=False): sz = '%dx%d'%(pmap.width(), pmap.height()) text = QVariant(src + '\n' + sz) scaled = pmap.scaled(150, 200, Qt.IgnoreAspectRatio, @@ -437,41 +472,118 @@ class CoversModel(QAbstractListModel): # {{{ if role == Qt.UserRole: return waiting return NONE + + def plugin_for_index(self, index): + row = index.row() if hasattr(index, 'row') else index + for k, v in self.plugin_map.iteritems(): + if v == row: + return k + + def clear_failed(self): + good = [] + pmap = {} + for i, x in enumerate(self.covers): + if not x[-1]: + good.append(x) + if i > 0: + plugin = self.plugin_for_index(i) + pmap[plugin] = len(good) - 1 + good = [x for x in self.covers if not x[-1]] + self.covers = good + self.plugin_map = pmap + self.reset() + + def index_for_plugin(self, plugin): + idx = self.plugin_map.get(plugin, 0) + return self.index(idx) + + def update_result(self, plugin, width, height, data): + try: + idx = self.plugin_map[plugin] + except: + return + pmap = QPixmap() + pmap.loadFromData(data) + if pmap.isNull(): + return + self.covers[idx] = self.get_item(plugin.name, pmap, waiting=False) + self.dataChanged.emit(self.index(idx), self.index(idx)) + + def cover_pmap(self, index): + row = index.row() + if row > 0 and row < len(self.covers): + pmap = self.books[row][2] + if pmap is not None and not pmap.isNull(): + return pmap + # }}} -class CoverDelegate(QStyledItemDelegate): +class CoverDelegate(QStyledItemDelegate): # {{{ needs_redraw = pyqtSignal() - def __init__(self, parent=None): + def __init__(self, parent): QStyledItemDelegate.__init__(self, parent) - self.movie = QMovie(I('spinner.gif')) - self.movie.frameChanged.connect(self.frame_changed) + self.angle = 0 + self.timer = QTimer(self) + self.timer.timeout.connect(self.frame_changed) + self.color = parent.palette().color(QPalette.WindowText) + self.spinner_width = 64 def frame_changed(self, *args): + self.angle = (self.angle+30)%360 self.needs_redraw.emit() - def start_movie(self): - self.movie.start() + def start_animation(self): + self.angle = 0 + self.timer.start(200) - def stop_movie(self): - self.movie.stop() + def stop_animation(self): + self.timer.stop() + + def draw_spinner(self, painter, rect): + width = rect.width() + + outer_radius = (width-1)*0.5 + inner_radius = (width-1)*0.5*0.38 + + capsule_height = outer_radius - inner_radius + capsule_width = int(capsule_height * (0.23 if width > 32 else 0.35)) + capsule_radius = capsule_width//2 + + painter.save() + painter.setRenderHint(painter.Antialiasing) + + for i in xrange(12): + color = QColor(self.color) + color.setAlphaF(1.0 - (i/12.0)) + painter.setPen(Qt.NoPen) + painter.setBrush(color) + painter.save() + painter.translate(rect.center()) + painter.rotate(self.angle - i*30.0) + painter.drawRoundedRect(-capsule_width*0.5, + -(inner_radius+capsule_height), capsule_width, + capsule_height, capsule_radius, capsule_radius) + painter.restore() + painter.restore() def paint(self, painter, option, index): - waiting = index.data(Qt.UserRole).toBool() - if waiting: - pixmap = self.movie.currentPixmap() - prect = pixmap.rect() - prect.moveCenter(option.rect.center()) - painter.drawPixmap(prect, pixmap, pixmap.rect()) QStyledItemDelegate.paint(self, painter, option, index) + if self.timer.isActive() and index.data(Qt.UserRole).toBool(): + rect = QRect(0, 0, self.spinner_width, self.spinner_width) + rect.moveCenter(option.rect.center()) + self.draw_spinner(painter, rect) +# }}} class CoversView(QListView): # {{{ - def __init__(self, log, current_cover, parent=None): + chosen = pyqtSignal() + + def __init__(self, current_cover, parent=None): QListView.__init__(self, parent) - self.m = CoversModel(log, current_cover, self) + self.m = CoversModel(current_cover, self) self.setModel(self.m) self.setFlow(self.LeftToRight) @@ -487,6 +599,8 @@ class CoversView(QListView): # {{{ self.delegate.needs_redraw.connect(self.viewport().update, type=Qt.QueuedConnection) + self.doubleClicked.connect(self.chosen, type=Qt.QueuedConnection) + def select(self, num): current = self.model().index(num) sm = self.selectionModel() @@ -494,15 +608,24 @@ class CoversView(QListView): # {{{ def start(self): self.select(0) - self.delegate.start_movie() + self.delegate.start_animation() + + def clear_failed(self): + plugin = self.m.plugin_for_index(self.currentIndex()) + self.m.clear_failed() + self.select(self.m.index_for_plugin(plugin).row()) # }}} -class CoverWidget(QWidget): # {{{ +class CoversWidget(QWidget): # {{{ + + chosen = pyqtSignal() + finished = pyqtSignal() def __init__(self, log, current_cover, parent=None): QWidget.__init__(self, parent) self.log = log + self.abort = Event() self.l = l = QGridLayout() self.setLayout(l) @@ -511,8 +634,10 @@ class CoverWidget(QWidget): # {{{ self.msg.setWordWrap(True) l.addWidget(self.msg, 0, 0) - self.covers_view = CoversView(log, current_cover, self) + self.covers_view = CoversView(current_cover, self) + self.covers_view.chosen.connect(self.chosen) l.addWidget(self.covers_view, 1, 0) + self.continue_processing = True def start(self, book, current_cover, title, authors): self.book, self.current_cover = book, current_cover @@ -521,6 +646,66 @@ class CoverWidget(QWidget): # {{{ self.msg.setText('

'+_('Downloading covers for %s, please wait...')%book.title) self.covers_view.start() + self.worker = CoverWorker(self.log, self.abort, self.title, + self.authors, book.identifiers) + self.worker.start() + QTimer.singleShot(50, self.check) + self.covers_view.setFocus(Qt.OtherFocusReason) + + def check(self): + if self.worker.is_alive() and not self.abort.is_set(): + QTimer.singleShot(50, self.check) + try: + self.process_result(self.worker.rq.get_nowait()) + except Empty: + pass + else: + self.process_results() + + def process_results(self): + while self.continue_processing: + try: + self.process_result(self.worker.rq.get_nowait()) + except Empty: + break + + self.covers_view.clear_failed() + + if self.worker.error is not None: + error_dialog(self, _('Download failed'), + _('Failed to download any covers, click' + ' "Show details" for details.'), + det_msg=self.worker.error, show=True) + + num = self.covers_view.model().rowCount() + if num < 2: + txt = _('Could not find any covers for %s')%self.book.title + else: + txt = _('Found %d covers of %s. Pick the one you like' + ' best.')%(num, self.title) + self.msg.setText(txt) + + self.finished.emit() + + def process_result(self, result): + if not self.continue_processing: + return + plugin, width, height, fmt, data = result + self.covers_view.model().update_result(plugin, width, height, data) + + def cleanup(self): + self.covers_view.delegate.stop_animation() + self.continue_processing = False + + def cancel(self): + self.continue_processing = False + self.abort.set() + + @property + def cover_pmap(self): + return self.covers_view.model().cover_pmap( + self.covers_view.currentIndex()) + # }}} class FullFetch(QDialog): # {{{ @@ -528,6 +713,7 @@ class FullFetch(QDialog): # {{{ def __init__(self, log, current_cover=None, parent=None): QDialog.__init__(self, parent) self.log, self.current_cover = log, current_cover + self.book = self.cover_pmap = None self.setWindowTitle(_('Downloading metadata...')) self.setWindowIcon(QIcon(I('metadata.png'))) @@ -554,17 +740,20 @@ class FullFetch(QDialog): # {{{ self.identify_widget.book_selected.connect(self.book_selected) self.stack.addWidget(self.identify_widget) - self.cover_widget = CoverWidget(self.log, self.current_cover, parent=self) - self.stack.addWidget(self.cover_widget) + self.covers_widget = CoversWidget(self.log, self.current_cover, parent=self) + self.covers_widget.chosen.connect(self.ok_clicked) + self.stack.addWidget(self.covers_widget) self.resize(850, 550) + self.finished.connect(self.cleanup) + def book_selected(self, book): self.next_button.setVisible(False) self.ok_button.setVisible(True) self.book = book self.stack.setCurrentIndex(1) - self.cover_widget.start(book, self.current_cover, + self.covers_widget.start(book, self.current_cover, self.title, self.authors) def accept(self): @@ -575,6 +764,9 @@ class FullFetch(QDialog): # {{{ self.identify_widget.cancel() return QDialog.reject(self) + def cleanup(self): + self.covers_widget.cleanup() + def identify_results_found(self): self.next_button.setEnabled(True) @@ -582,7 +774,8 @@ class FullFetch(QDialog): # {{{ self.identify_widget.get_result() def ok_clicked(self, *args): - pass + self.cover_pmap = self.covers_widget.cover_pmap + QDialog.accept(self) def start(self, title=None, authors=None, identifiers={}): self.title, self.authors = title, authors @@ -592,6 +785,7 @@ class FullFetch(QDialog): # {{{ # }}} if __name__ == '__main__': + DEVELOP_DIALOG = True app = QApplication([]) d = FullFetch(Log()) d.start(title='great gatsby', authors=['Fitzgerald']) From 6bf57f68a58a58f03570d354936a09f73882071a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Apr 2011 16:18:27 -0600 Subject: [PATCH 18/20] ... --- src/calibre/gui2/metadata/single_download.py | 27 +++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/calibre/gui2/metadata/single_download.py b/src/calibre/gui2/metadata/single_download.py index c397a8660a..d725229344 100644 --- a/src/calibre/gui2/metadata/single_download.py +++ b/src/calibre/gui2/metadata/single_download.py @@ -417,8 +417,11 @@ class CoverWorker(Thread): # {{{ self.error = None def fake_run(self): + images = ['donate.png', 'config.png', 'column.png', 'eject.png', ] import time time.sleep(2) + for pl, im in zip(metadata_plugins(['cover']), images): + self.rq.put((pl, 1, 1, 'png', I(im, data=True))) def run(self): try: @@ -509,10 +512,10 @@ class CoversModel(QAbstractListModel): # {{{ self.covers[idx] = self.get_item(plugin.name, pmap, waiting=False) self.dataChanged.emit(self.index(idx), self.index(idx)) - def cover_pmap(self, index): + def cover_pixmap(self, index): row = index.row() if row > 0 and row < len(self.covers): - pmap = self.books[row][2] + pmap = self.covers[row][2] if pmap is not None and not pmap.isNull(): return pmap @@ -682,7 +685,7 @@ class CoversWidget(QWidget): # {{{ txt = _('Could not find any covers for %s')%self.book.title else: txt = _('Found %d covers of %s. Pick the one you like' - ' best.')%(num, self.title) + ' best.')%(num-1, self.title) self.msg.setText(txt) self.finished.emit() @@ -701,9 +704,8 @@ class CoversWidget(QWidget): # {{{ self.continue_processing = False self.abort.set() - @property - def cover_pmap(self): - return self.covers_view.model().cover_pmap( + def cover_pixmap(self): + return self.covers_view.model().cover_pixmap( self.covers_view.currentIndex()) # }}} @@ -713,7 +715,7 @@ class FullFetch(QDialog): # {{{ def __init__(self, log, current_cover=None, parent=None): QDialog.__init__(self, parent) self.log, self.current_cover = log, current_cover - self.book = self.cover_pmap = None + self.book = self.cover_pixmap = None self.setWindowTitle(_('Downloading metadata...')) self.setWindowIcon(QIcon(I('metadata.png'))) @@ -774,8 +776,15 @@ class FullFetch(QDialog): # {{{ self.identify_widget.get_result() def ok_clicked(self, *args): - self.cover_pmap = self.covers_widget.cover_pmap - QDialog.accept(self) + self.cover_pixmap = self.covers_widget.cover_pixmap() + if DEVELOP_DIALOG: + if self.cover_pixmap is not None: + self.w = QLabel() + self.w.setPixmap(self.cover_pixmap) + self.stack.addWidget(self.w) + self.stack.setCurrentIndex(2) + else: + QDialog.accept(self) def start(self, title=None, authors=None, identifiers={}): self.title, self.authors = title, authors From d042ae6b7454801c0fcc6d64147de21d3ad4342f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Apr 2011 16:22:04 -0600 Subject: [PATCH 19/20] ... --- src/calibre/gui2/metadata/single_download.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/metadata/single_download.py b/src/calibre/gui2/metadata/single_download.py index d725229344..83386445c5 100644 --- a/src/calibre/gui2/metadata/single_download.py +++ b/src/calibre/gui2/metadata/single_download.py @@ -705,8 +705,15 @@ class CoversWidget(QWidget): # {{{ self.abort.set() def cover_pixmap(self): - return self.covers_view.model().cover_pixmap( - self.covers_view.currentIndex()) + idx = None + for i in self.covers_view.selectionModel().selectedIndexes(): + if i.isValid(): + idx = i + break + if idx is None: + idx = self.covers_view.currentIndex() + return self.covers_view.model().cover_pixmap(idx) + # }}} From eaf6060421f66bef2f1c7aefcad0ec1321f88eb6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 9 Apr 2011 16:53:10 -0600 Subject: [PATCH 20/20] Log viewer --- src/calibre/gui2/metadata/single_download.py | 179 ++++++++++++------- 1 file changed, 113 insertions(+), 66 deletions(-) diff --git a/src/calibre/gui2/metadata/single_download.py b/src/calibre/gui2/metadata/single_download.py index 83386445c5..35c66340c6 100644 --- a/src/calibre/gui2/metadata/single_download.py +++ b/src/calibre/gui2/metadata/single_download.py @@ -15,7 +15,7 @@ from PyQt4.Qt import (QStyledItemDelegate, QTextDocument, QRectF, QIcon, Qt, QStyle, QApplication, QDialog, QVBoxLayout, QLabel, QDialogButtonBox, QStackedWidget, QWidget, QTableView, QGridLayout, QFontInfo, QPalette, QTimer, pyqtSignal, QAbstractTableModel, QVariant, QSize, QListView, - QPixmap, QAbstractListModel, QColor, QRect) + QPixmap, QAbstractListModel, QColor, QRect, QTextBrowser) from PyQt4.QtWebKit import QWebView from calibre.customize.ui import metadata_plugins @@ -28,7 +28,7 @@ from calibre.utils.date import utcnow, fromordinal, format_date from calibre.library.comments import comments_to_html from calibre import force_unicode -DEVELOP_DIALOG = False +DEBUG_DIALOG = False class RichTextDelegate(QStyledItemDelegate): # {{{ @@ -57,6 +57,65 @@ class RichTextDelegate(QStyledItemDelegate): # {{{ painter.restore() # }}} +class CoverDelegate(QStyledItemDelegate): # {{{ + + needs_redraw = pyqtSignal() + + def __init__(self, parent): + QStyledItemDelegate.__init__(self, parent) + + self.angle = 0 + self.timer = QTimer(self) + self.timer.timeout.connect(self.frame_changed) + self.color = parent.palette().color(QPalette.WindowText) + self.spinner_width = 64 + + def frame_changed(self, *args): + self.angle = (self.angle+30)%360 + self.needs_redraw.emit() + + def start_animation(self): + self.angle = 0 + self.timer.start(200) + + def stop_animation(self): + self.timer.stop() + + def draw_spinner(self, painter, rect): + width = rect.width() + + outer_radius = (width-1)*0.5 + inner_radius = (width-1)*0.5*0.38 + + capsule_height = outer_radius - inner_radius + capsule_width = int(capsule_height * (0.23 if width > 32 else 0.35)) + capsule_radius = capsule_width//2 + + painter.save() + painter.setRenderHint(painter.Antialiasing) + + for i in xrange(12): + color = QColor(self.color) + color.setAlphaF(1.0 - (i/12.0)) + painter.setPen(Qt.NoPen) + painter.setBrush(color) + painter.save() + painter.translate(rect.center()) + painter.rotate(self.angle - i*30.0) + painter.drawRoundedRect(-capsule_width*0.5, + -(inner_radius+capsule_height), capsule_width, + capsule_height, capsule_radius, capsule_radius) + painter.restore() + painter.restore() + + def paint(self, painter, option, index): + QStyledItemDelegate.paint(self, painter, option, index) + if self.timer.isActive() and index.data(Qt.UserRole).toBool(): + rect = QRect(0, 0, self.spinner_width, self.spinner_width) + rect.moveCenter(option.rect.center()) + self.draw_spinner(painter, rect) +# }}} + class ResultsModel(QAbstractTableModel): # {{{ COLUMNS = ( @@ -273,7 +332,7 @@ class IdentifyWorker(Thread): # {{{ def run(self): try: - if DEVELOP_DIALOG: + if DEBUG_DIALOG: self.results = self.sample_results() else: self.results = identify(self.log, self.abort, title=self.title, @@ -425,7 +484,7 @@ class CoverWorker(Thread): # {{{ def run(self): try: - if DEVELOP_DIALOG: + if DEBUG_DIALOG: self.fake_run() else: from calibre.ebooks.metadata.sources.covers import run_download @@ -521,65 +580,6 @@ class CoversModel(QAbstractListModel): # {{{ # }}} -class CoverDelegate(QStyledItemDelegate): # {{{ - - needs_redraw = pyqtSignal() - - def __init__(self, parent): - QStyledItemDelegate.__init__(self, parent) - - self.angle = 0 - self.timer = QTimer(self) - self.timer.timeout.connect(self.frame_changed) - self.color = parent.palette().color(QPalette.WindowText) - self.spinner_width = 64 - - def frame_changed(self, *args): - self.angle = (self.angle+30)%360 - self.needs_redraw.emit() - - def start_animation(self): - self.angle = 0 - self.timer.start(200) - - def stop_animation(self): - self.timer.stop() - - def draw_spinner(self, painter, rect): - width = rect.width() - - outer_radius = (width-1)*0.5 - inner_radius = (width-1)*0.5*0.38 - - capsule_height = outer_radius - inner_radius - capsule_width = int(capsule_height * (0.23 if width > 32 else 0.35)) - capsule_radius = capsule_width//2 - - painter.save() - painter.setRenderHint(painter.Antialiasing) - - for i in xrange(12): - color = QColor(self.color) - color.setAlphaF(1.0 - (i/12.0)) - painter.setPen(Qt.NoPen) - painter.setBrush(color) - painter.save() - painter.translate(rect.center()) - painter.rotate(self.angle - i*30.0) - painter.drawRoundedRect(-capsule_width*0.5, - -(inner_radius+capsule_height), capsule_width, - capsule_height, capsule_radius, capsule_radius) - painter.restore() - painter.restore() - - def paint(self, painter, option, index): - QStyledItemDelegate.paint(self, painter, option, index) - if self.timer.isActive() and index.data(Qt.UserRole).toBool(): - rect = QRect(0, 0, self.spinner_width, self.spinner_width) - rect.moveCenter(option.rect.center()) - self.draw_spinner(painter, rect) -# }}} - class CoversView(QListView): # {{{ chosen = pyqtSignal() @@ -714,6 +714,46 @@ class CoversWidget(QWidget): # {{{ idx = self.covers_view.currentIndex() return self.covers_view.model().cover_pixmap(idx) +# }}} + +class LogViewer(QDialog): # {{{ + + def __init__(self, log, parent=None): + QDialog.__init__(self, parent) + self.log = log + self.l = l = QVBoxLayout() + self.setLayout(l) + + self.tb = QTextBrowser(self) + l.addWidget(self.tb) + + self.bb = QDialogButtonBox(QDialogButtonBox.Close) + l.addWidget(self.bb) + self.bb.rejected.connect(self.reject) + self.bb.accepted.connect(self.accept) + + self.setWindowTitle(_('Download log')) + self.setWindowIcon(QIcon(I('debug.png'))) + self.resize(QSize(800, 400)) + + self.keep_updating = True + self.last_html = None + self.finished.connect(self.stop) + QTimer.singleShot(1000, self.update_log) + + self.show() + + def stop(self, *args): + self.keep_updating = False + + def update_log(self): + if not self.keep_updating: + return + html = self.log.html + if html != self.last_html: + self.last_html = html + self.tb.setHtml('

%s
'%html) + QTimer.singleShot(1000, self.update_log) # }}} @@ -738,10 +778,14 @@ class FullFetch(QDialog): # {{{ self.next_button = self.bb.addButton(_('Next'), self.bb.AcceptRole) self.next_button.setDefault(True) self.next_button.setEnabled(False) + self.next_button.setIcon(QIcon(I('ok.png'))) self.next_button.clicked.connect(self.next_clicked) self.ok_button = self.bb.button(self.bb.Ok) - self.ok_button.setVisible(False) self.ok_button.clicked.connect(self.ok_clicked) + self.log_button = self.bb.addButton(_('View log'), self.bb.ActionRole) + self.log_button.clicked.connect(self.view_log) + self.log_button.setIcon(QIcon(I('debug.png'))) + self.ok_button.setVisible(False) self.identify_widget = IdentifyWidget(log, self) self.identify_widget.rejected.connect(self.reject) @@ -757,6 +801,9 @@ class FullFetch(QDialog): # {{{ self.finished.connect(self.cleanup) + def view_log(self): + self._lv = LogViewer(self.log, self) + def book_selected(self, book): self.next_button.setVisible(False) self.ok_button.setVisible(True) @@ -784,7 +831,7 @@ class FullFetch(QDialog): # {{{ def ok_clicked(self, *args): self.cover_pixmap = self.covers_widget.cover_pixmap() - if DEVELOP_DIALOG: + if DEBUG_DIALOG: if self.cover_pixmap is not None: self.w = QLabel() self.w.setPixmap(self.cover_pixmap) @@ -801,7 +848,7 @@ class FullFetch(QDialog): # {{{ # }}} if __name__ == '__main__': - DEVELOP_DIALOG = True + DEBUG_DIALOG = True app = QApplication([]) d = FullFetch(Log()) d.start(title='great gatsby', authors=['Fitzgerald'])