class PrincePDFDialog(QDialog): prince_log = '' # GUI definition def __init__(self, gui, icon, do_user_config): QDialog.__init__(self, gui) self.icon = icon self.gui = gui self.do_user_config = do_user_config self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowTitle(_('Prince PDF')) self.setWindowIcon(icon) self.l = QGridLayout() self.setLayout(self.l) self.image = QLabel() self.image.setPixmap(icon.pixmap(120, 120)) self.l.addWidget(self.image, 1, 1, 4, 1, Qt.AlignVCenter) self.convert_to_PDF_button = QPushButton(_('Convert to &PDF'), self) self.convert_to_PDF_button.clicked.connect(self.convert_to_PDF) self.convert_to_PDF_button.setDefault(True) self.convert_to_PDF_button.setToolTip(_('<qt>Start the conversion process</qt>')) self.l.addWidget(self.convert_to_PDF_button, 1, 2, Qt.AlignVCenter) self.view_log = QPushButton(_('&View log'), self) self.view_log.clicked.connect(self.show_log) self.view_log.setToolTip(_('<qt>Display the log from the last Prince run</qt>')) self.l.addWidget(self.view_log, 2, 2, Qt.AlignVCenter) self.view_log.hide() self.conf_button = QPushButton(_('Con&figure'), self) self.conf_button.clicked.connect(self.config) self.conf_button.setToolTip(_('<qt>Configure this plugin</qt>')) self.l.addWidget(self.conf_button, 4, 2, Qt.AlignVCenter) self.info = QLabel() self.l.addWidget(self.info, 5, 1, 1, -1) self.suggestion = QLabel() self.suggestion.setAlignment(Qt.AlignCenter) self.l.addWidget(self.suggestion, 6, 1, 1, -1) self.l.setColumnStretch(1, 1) self.l.setColumnStretch(2, 10) self.l.setRowStretch(1, 1) self.l.setRowStretch(2, 1) self.l.setRowStretch(3, 10) self.l.setRowStretch(4, 1) self.l.setRowStretch(5, 1) self.l.setRowStretch(6, 1) self.l.setRowMinimumHeight(1, 1) self.l.setRowMinimumHeight(2, 1) self.l.setRowMinimumHeight(3, 1) self.l.setRowMinimumHeight(4, 1) self.l.setRowMinimumHeight(5, 1) self.l.setRowMinimumHeight(6, 1) self.buttons = QDialogButtonBox(QDialogButtonBox.Close | QDialogButtonBox.Help) self.l.addWidget(self.buttons, 7, 1, 1, -1) self.buttons.rejected.connect(self.reject) self.buttons.helpRequested.connect(self.about) self.adjustSize() # Update the info now and every time the selection or the data changes self.gui.library_view.model().dataChanged.connect(self.update_info) self.gui.library_view.selectionModel().selectionChanged.connect(self.update_info) self.update_info() def update_info(self): ''' Update the info on top of the window ''' self.db = self.gui.current_db # Get selected rows rows = self.gui.library_view.selectionModel().selectedRows() if not rows or len(rows) == 0: self.info.setText(_('<b>No books selected</b>')) self.info.setAlignment(Qt.AlignCenter) self.suggestion.setText(_('Select one single book')) self.selected = None self.convert_to_PDF_button.setEnabled(False) elif len(rows) > 1: self.info.setText(_('<b>Many books selected</b>')) self.info.setAlignment(Qt.AlignCenter) self.suggestion.setText(_('Select one single book')) self.selected = None self.convert_to_PDF_button.setEnabled(False) else: # If there is only one selected book, enable conversion # and show list of available formats (formats in prefs are bold) book_id = self.gui.library_view.model().id(rows[0]) fmts = self.db.formats(book_id, index_is_id=True) if (fmts): fmts = fmts.split(',') else: fmts = [_('<i>none</i>')] available = False for i,fmt in enumerate(fmts): fmts[i] = fmt.lower() if fmts[i] in prefs['formats']: fmts[i] = '<b>%s</b>' % fmts[i] available = True self.info.setText(_('Available formats: %s') % ', '.join(fmts)) self.info.setAlignment(Qt.AlignLeft) # Conversion enabled only if some "preferred" format is found if (available): self.suggestion.setText(_('Ready')) self.selected = book_id self.convert_to_PDF_button.setEnabled(True) else: self.suggestion.setText(_('No preferred format available')) self.selected = None self.convert_to_PDF_button.setEnabled(False) def about(self): ''' Display a short help message ''' from os.path import join from calibre.ptempfile import TemporaryDirectory from calibre.gui2.dialogs.message_box import MessageBox from calibre_plugins.prince_pdf.help import help_txt, license_txt from calibre_plugins.prince_pdf import PrincePDFPlugin from calibre_plugins.prince_pdf import __license__ author = PrincePDFPlugin.author version = "%i.%i.%i" % PrincePDFPlugin.version license = __license__ with TemporaryDirectory('xxx') as tdir: for x in ('prince_icon.png', 'small_icon.png'): with open(join(tdir, x),'w') as f: f.write(get_resources('images/' + x)) help_box = MessageBox(type_ = MessageBox.INFO, \ title = _('About the Prince PDF Plugin'), \ msg = help_txt % {'author':author, 'version':version, 'license':license, 'dir':tdir, 'code':'style="font-family:monospace ; font-weight:bold"'}, \ det_msg = 'Copyright \u00a9 %s\n%s' % (__copyright__, license_txt), \ q_icon = self.icon, \ show_copy_button = False) #help_box.gridLayout.addWidget(help_box.icon_widget,0,0,Qt.AlignTop) help_box.gridLayout.setAlignment(help_box.icon_widget,Qt.AlignTop) help_box.exec_() def convert_to_PDF(self): ''' Unpack and convert the currently selected book to PDF ''' from calibre.gui2 import error_dialog from calibre.constants import DEBUG # Get available formats book_id = self.selected fmts = self.db.formats(book_id, index_is_id=True) fmts = fmts.lower().split(',') # Process only the first format matching the 'formats' configuration option for fmt in prefs['formats']: fmt = fmt.lower() if (not fmt in fmts): continue mi = self.db.get_metadata(book_id, index_is_id=True, get_cover=False) pdf_base_file = self.get_filename(book_id, mi) # This is the actual code: if DEBUG: print('===========') # Unpack the book and call the conversion dialog (opf, oeb) = self.unpack(book_id, fmt) if (opf == None or oeb == None): return error_dialog(self.gui, _('Cannot convert to PDF'), _('Format not supported: %s') % fmt, show=True) convert_dialog = ConvertDialog(mi, fmt, opf, oeb, self.icon) convert_dialog.pdf_file = pdf_base_file pdf_file = '' if (convert_dialog.exec_()): pdf_file = convert_dialog.pdf_file self.prince_log = convert_dialog.prince_log # After the dialog returns, pdf_file has the output file path, # and prince_log has the Prince console output if DEBUG: print(_('PDF file: %s') % pdf_file) # If there is any log, enable the View log button if (self.prince_log): self.view_log.show() log_msg = _(' Check the log.') else: self.view_log.hide() log_msg = '' # If the conversion failed, pdf_file will be None, if (pdf_file == None): error_dialog(self.gui, _('Could not convert to PDF'), _('The conversion failed.') + log_msg , show=True) # If the user cancelled the dialog, pdf_file will be '' if (pdf_file): # Set the metadata in the PDF and add it or save it try: self.set_pdf_metadata(mi, pdf_file) except: error_dialog(self.gui, _('Could not convert to PDF'), _("Error reading or writing the PDF file:\n%s" % pdf_file), show=True) return if (prefs['add_book']): self.add_pdf(book_id, pdf_file, ('pdf' in fmts)) else: self.save_pdf(pdf_file, pdf_base_file) if DEBUG: print('===========') return # No matching format in the book return error_dialog(self.gui, _('Cannot convert to PDF'), _('No supported format available'), show=True) def show_log(self): ''' Display the Prince log dialog ''' msg = LogDialog(self.prince_log, self.icon) msg.exec_() def config(self): ''' Display the configuration dialog ''' self.do_user_config(parent=self) def get_filename(self, book_id, mi): ''' Obtain a filename from the save_to_disk template :param book_id: The book identifier :param mi: The book metadata ''' from os.path import join from calibre.library.save_to_disk import get_components, config from calibre import sanitize_file_name_unicode from calibre.utils.filenames import ascii_filename opts = config().parse() components = get_components(opts.template, mi, book_id, opts.timefmt, sanitize_func=(ascii_filename if opts.asciiize else sanitize_file_name_unicode), to_lowercase=opts.to_lowercase, replace_whitespace=opts.replace_whitespace) base_path = join(*components) return '%s.pdf' % base_path def unpack(self, book_id, fmt): ''' Unpack the book in a temporary directory :param book_id: The book identifier :param fmt: The format to unpack ''' from calibre.constants import DEBUG from calibre.ptempfile import PersistentTemporaryDirectory from calibre.ebooks.tweak import get_tools from calibre.ebooks.oeb.base import OEBBook from calibre.ebooks.oeb.reader import OEBReader from calibre.utils.logging import default_log from calibre_plugins.prince_pdf.dummy_preprocessor import dummy_preprocessor book_file = self.db.format(book_id, fmt, index_is_id=True, as_path=True) if DEBUG: print(_('Unpacking book...')) tdir = PersistentTemporaryDirectory('_unpack') exploder = get_tools(fmt)[0] if (exploder == None): return (None, None) opf = exploder(book_file, tdir) html_preprocessor = dummy_preprocessor() css_preprocessor = dummy_preprocessor() oeb = OEBBook(default_log, html_preprocessor, css_preprocessor) OEBReader()(oeb, opf) return (opf, oeb) def set_pdf_metadata(self, mi, pdf_file): ''' Set the metadata in the PDF file :param mi: The book metadata :param pdf_file: The path to the PDF file ''' from calibre.constants import DEBUG from calibre.ebooks.metadata.pdf import set_metadata if DEBUG: print(_('Setting metadata...')) pdf_stream = open(pdf_file, 'r+b') set_metadata(pdf_stream, mi) pdf_stream.close() def add_pdf(self, book_id, pdf_file, exists): ''' Add the PDF file to the book record, asking for replacement :param book_id: The book identifier :param pdf_file: The path to the PDF file :param exists: True if there is already a PDF in the book ''' from calibre.constants import DEBUG from calibre.gui2.dialogs.message_box import MessageBox add_it = True if (exists): msg = MessageBox(MessageBox.QUESTION, _('Existing format'), _('The selected book already contains a PDF format. Are you sure you want to replace it?'), _("The temporary file can be found in:\n%s") % pdf_file) msg.toggle_det_msg() add_it = (msg.exec_()) if (add_it): if DEBUG: print(_('Adding PDF...')) self.db.new_api.add_format(book_id, 'pdf', pdf_file) self.gui.library_view.model().refresh_ids([book_id]) self.gui.library_view.refresh_book_details() self.gui.tags_view.recount() def save_pdf(self, pdf_file, pdf_base_file): ''' Save the PDF file in the final location :param pdf_file: The path to the PDF file :param pdf_base_file: The desired file name and relative path ''' from os import rename, makedirs from os.path import basename, dirname, join, exists from calibre.constants import DEBUG from calibre.gui2 import choose_dir from calibre.gui2.dialogs.message_box import MessageBox from calibre.gui2 import error_dialog path = choose_dir(self.gui, 'save to disk dialog', _('Choose destination directory')) if not path: return save_file = join(path, pdf_base_file) base_dir = dirname(save_file) try: makedirs(base_dir) except BaseException: if not exists(base_dir): raise try: rename(pdf_file, save_file) except: error_dialog(self.gui, _('Could not save PDF'), _("Error writing the PDF file:\n%s" % save_file), show=True) return if DEBUG: print(save_file) MessageBox(MessageBox.INFO, _('File saved'), _("PDF file saved in:\n%s") % save_file).exec_()
def setup_store_checks(self): first_run = self.config.get('first_run', True) # Add check boxes for each store so the user # can disable searching specific stores on a # per search basis. existing = {} for n in self.store_checks: existing[n] = self.store_checks[n].isChecked() self.store_checks = {} stores_check_widget = QWidget() store_list_layout = QGridLayout() stores_check_widget.setLayout(store_list_layout) icon = QIcon(I('donate.png')) for i, x in enumerate(sorted(self.gui.istores.keys(), key=lambda x: x.lower())): cbox = QCheckBox(x) cbox.setChecked(existing.get(x, first_run)) store_list_layout.addWidget(cbox, i, 0, 1, 1) if self.gui.istores[x].base_plugin.affiliate: iw = QLabel(self) iw.setToolTip('<p>' + _('Buying from this store supports the calibre developer: %s</p>') % self.gui.istores[x].base_plugin.author + '</p>') iw.setPixmap(icon.pixmap(16, 16)) store_list_layout.addWidget(iw, i, 1, 1, 1) self.store_checks[x] = cbox store_list_layout.setRowStretch(store_list_layout.rowCount(), 10) self.store_list.setWidget(stores_check_widget) self.config['first_run'] = False
class MetadataGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(MetadataGroupBox, self).__init__(parent, device) self.setTitle(_("Update metadata on the device")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.setCheckable(True) self.setChecked(device.get_pref('update_device_metadata')) self.setToolTip(wrap_msg(_('Update the metadata on the device when it is connected. ' 'Be careful when doing this as it will take time and could make the initial connection take a long time.'))) self.update_series_checkbox = create_checkbox( _("Set Series information"), _('The book lists on the Kobo devices can display series information. ' 'This is not read by the device from the sideloaded books. ' 'Series information can only be added to the device after the book has been processed by the device. ' 'Enable if you wish to set series information.'), device.get_pref('update_series') ) self.options_layout.addWidget(self.update_series_checkbox, 0, 0, 1, 1) self.options_layout.setRowStretch(1, 1) @property def update_series(self): return self.update_series_checkbox.isChecked() @property def update_device_metadata(self): return self.isChecked()
class MetadataGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(MetadataGroupBox, self).__init__(parent, device) self.setTitle(_("Update metadata on the device")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.setCheckable(True) self.setChecked(device.get_pref('update_device_metadata')) self.setToolTip( wrap_msg( _('Update the metadata on the device when it is connected. ' 'Be careful when doing this as it will take time and could make the initial connection take a long time.' ))) self.update_series_checkbox = create_checkbox( _("Set series information"), _('The book lists on the Kobo devices can display series information. ' 'This is not read by the device from the sideloaded books. ' 'Series information can only be added to the device after the book has been processed by the device. ' 'Enable if you wish to set series information.'), device.get_pref('update_series')) self.options_layout.addWidget(self.update_series_checkbox, 0, 0, 1, 1) self.options_layout.setRowStretch(1, 1) @property def update_series(self): return self.update_series_checkbox.isChecked() @property def update_device_metadata(self): return self.isChecked()
def setup_store_checks(self): first_run = self.config.get('first_run', True) # Add check boxes for each store so the user # can disable searching specific stores on a # per search basis. existing = {} for n in self.store_checks: existing[n] = self.store_checks[n].isChecked() self.store_checks = {} stores_check_widget = QWidget() store_list_layout = QGridLayout() stores_check_widget.setLayout(store_list_layout) icon = QIcon(I('donate.png')) for i, x in enumerate( sorted(self.gui.istores.keys(), key=lambda x: x.lower())): cbox = QCheckBox(x) cbox.setChecked(existing.get(x, first_run)) store_list_layout.addWidget(cbox, i, 0, 1, 1) if self.gui.istores[x].base_plugin.affiliate: iw = QLabel(self) iw.setToolTip('<p>' + _( 'Buying from this store supports the calibre developer: %s</p>' ) % self.gui.istores[x].base_plugin.author + '</p>') iw.setPixmap(icon.pixmap(16, 16)) store_list_layout.addWidget(iw, i, 1, 1, 1) self.store_checks[x] = cbox store_list_layout.setRowStretch(store_list_layout.rowCount(), 10) self.store_list.setWidget(stores_check_widget) self.config['first_run'] = False
class DownloadStatus(QScrollArea): def __init__(self, parent=None): QScrollArea.__init__(self, parent) self.setWidgetResizable(True) self.w = QWidget(self) self.l = QGridLayout(self.w) self.setWidget(self.w) def __call__(self, resources): self.url_map = {} self.labels = [] for url in resources: p = self.url_map[url] = QProgressBar(self.w) p.setRange(0, 0) self.l.addWidget(p, self.l.rowCount(), 0) la = QLabel('\xa0' + url) self.labels.append(la) self.l.addWidget(la, self.l.rowCount() - 1, 1) self.l.addWidget(QLabel('')) self.l.setRowStretch(self.l.rowCount() - 1, 10) def progress(self, url, done, total): p = self.url_map.get(url) if p is not None: if total > 0: p.setRange(0, total) p.setValue(done) else: p.setRange(0, 0)
class BookUploadsGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(BookUploadsGroupBox, self).__init__(parent, device) self.setTitle(_("Book Uploading")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.modify_css_checkbox = create_checkbox( _("Modify CSS"), _('This allows addition of user CSS rules and removal of some CSS. ' 'When sending a book, the driver adds the contents of {0} to all stylesheets in the ePub. ' 'This file is searched for in the root directory of the main memory of the device. ' 'As well as this, if the file contains settings for the "orphans" or "widows", ' 'these are removed for all styles in the original stylesheet.').format(device.KOBO_EXTRA_CSSFILE), device.get_pref('modify_css') ) self.options_layout.addWidget(self.modify_css_checkbox, 0, 0, 1, 2) self.options_layout.setRowStretch(1, 1) @property def modify_css(self): return self.modify_css_checkbox.isChecked()
class DownloadStatus(QScrollArea): def __init__(self, parent=None): QScrollArea.__init__(self, parent) self.setWidgetResizable(True) self.w = QWidget(self) self.l = QGridLayout(self.w) self.setWidget(self.w) def __call__(self, resources): self.url_map = {} self.labels = [] for url in resources: p = self.url_map[url] = QProgressBar(self.w) p.setRange(0, 0) self.l.addWidget(p, self.l.rowCount(), 0) la = QLabel('\xa0' + url) self.labels.append(la) self.l.addWidget(la, self.l.rowCount()-1, 1) self.l.addWidget(QLabel('')) self.l.setRowStretch(self.l.rowCount()-1, 10) def progress(self, url, done, total): p = self.url_map.get(url) if p is not None: if total > 0: p.setRange(0, total) p.setValue(done) else: p.setRange(0, 0)
class CollectionsGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(CollectionsGroupBox, self).__init__(parent, device) self.setTitle(_("Collections")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.setCheckable(True) self.setChecked(device.get_pref('manage_collections')) self.setToolTip( wrap_msg( _('Create new bookshelves on the Kobo if they do not exist. This is only for firmware V2.0.0 or later.' ))) self.collections_columns_label = QLabel(_('Collections Columns')) self.collections_columns_edit = QLineEdit(self) self.collections_columns_edit.setToolTip( _('The Kobo from firmware V2.0.0 supports bookshelves.' ' These are created on the Kobo. ' + 'Specify a tags type column for automatic management.')) self.collections_columns_edit.setText( device.get_pref('collections_columns')) self.create_collections_checkbox = create_checkbox( _("Create Collections"), _('Create new bookshelves on the Kobo if they do not exist. This is only for firmware V2.0.0 or later.' ), device.get_pref('create_collections')) self.delete_empty_collections_checkbox = create_checkbox( _('Delete Empty Bookshelves'), _('Delete any empty bookshelves from the Kobo when syncing is finished. This is only for firmware V2.0.0 or later.' ), device.get_pref('delete_empty_collections')) self.options_layout.addWidget(self.collections_columns_label, 1, 0, 1, 1) self.options_layout.addWidget(self.collections_columns_edit, 1, 1, 1, 1) self.options_layout.addWidget(self.create_collections_checkbox, 2, 0, 1, 2) self.options_layout.addWidget(self.delete_empty_collections_checkbox, 3, 0, 1, 2) self.options_layout.setRowStretch(4, 1) @property def manage_collections(self): return self.isChecked() @property def collections_columns(self): return self.collections_columns_edit.text().strip() @property def create_collections(self): return self.create_collections_checkbox.isChecked() @property def delete_empty_collections(self): return self.delete_empty_collections_checkbox.isChecked()
class DeviceListGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(DeviceListGroupBox, self).__init__(parent, device) self.setTitle(_("Show as on device")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.show_recommendations_checkbox = create_checkbox( _("Show Recommendations"), _('Kobo shows recommendations on the device. In some cases these have ' 'files but in other cases they are just pointers to the web site to buy. ' 'Enable if you wish to see/delete them.'), device.get_pref('show_recommendations') ) self.show_archived_books_checkbox = create_checkbox( _("Show archived books"), _('Archived books are listed on the device but need to be downloaded to read.' ' Use this option to show these books and match them with books in the calibre library.'), device.get_pref('show_archived_books') ) self.show_previews_checkbox = create_checkbox( _('Show Previews'), _('Kobo previews are included on the Touch and some other versions' ' by default they are no longer displayed as there is no good reason to ' 'see them. Enable if you wish to see/delete them.'), device.get_pref('show_previews') ) self.options_layout.addWidget(self.show_recommendations_checkbox, 0, 0, 1, 1) self.options_layout.addWidget(self.show_archived_books_checkbox, 1, 0, 1, 1) self.options_layout.addWidget(self.show_previews_checkbox, 2, 0, 1, 1) self.options_layout.setRowStretch(3, 1) @property def show_recommendations(self): return self.show_recommendations_checkbox.isChecked() @property def show_archived_books(self): return self.show_archived_books_checkbox.isChecked() @property def show_previews(self): return self.show_previews_checkbox.isChecked()
class AdvancedGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(AdvancedGroupBox, self).__init__(parent, device, _("Advanced options")) # self.setTitle(_("Advanced Options")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.support_newer_firmware_checkbox = create_checkbox( _("Attempt to support newer firmware"), _('Kobo routinely updates the firmware and the ' 'database version. With this option calibre will attempt ' 'to perform full read-write functionality - Here be Dragons!! ' 'Enable only if you are comfortable with restoring your kobo ' 'to factory defaults and testing software. ' 'This driver supports firmware V2.x.x and DBVersion up to ') + unicode(device.supported_dbversion), device.get_pref('support_newer_firmware')) self.debugging_title_checkbox = create_checkbox( _("Title to test when debugging"), _('Part of title of a book that can be used when doing some tests for debugging. ' 'The test is to see if the string is contained in the title of a book. ' 'The better the match, the less extraneous output.'), device.get_pref('debugging_title')) self.debugging_title_label = QLabel(_('Title to test when debugging:')) self.debugging_title_edit = QLineEdit(self) self.debugging_title_edit.setToolTip( _('Part of title of a book that can be used when doing some tests for debugging. ' 'The test is to see if the string is contained in the title of a book. ' 'The better the match, the less extraneous output.')) self.debugging_title_edit.setText(device.get_pref('debugging_title')) self.debugging_title_label.setBuddy(self.debugging_title_edit) self.options_layout.addWidget(self.support_newer_firmware_checkbox, 0, 0, 1, 2) self.options_layout.addWidget(self.debugging_title_label, 1, 0, 1, 1) self.options_layout.addWidget(self.debugging_title_edit, 1, 1, 1, 1) self.options_layout.setRowStretch(2, 2) @property def support_newer_firmware(self): return self.support_newer_firmware_checkbox.isChecked() @property def debugging_title(self): return self.debugging_title_edit.text().strip()
class AdvancedGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(AdvancedGroupBox, self).__init__(parent, device, _("Advanced Options")) # self.setTitle(_("Advanced Options")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.support_newer_firmware_checkbox = create_checkbox( _("Attempt to support newer firmware"), _('Kobo routinely updates the firmware and the ' 'database version. With this option Calibre will attempt ' 'to perform full read-write functionality - Here be Dragons!! ' 'Enable only if you are comfortable with restoring your kobo ' 'to factory defaults and testing software. ' 'This driver supports firmware V2.x.x and DBVersion up to ') + unicode( device.supported_dbversion), device.get_pref('support_newer_firmware') ) self.debugging_title_checkbox = create_checkbox( _("Title to test when debugging"), _('Part of title of a book that can be used when doing some tests for debugging. ' 'The test is to see if the string is contained in the title of a book. ' 'The better the match, the less extraneous output.'), device.get_pref('debugging_title') ) self.debugging_title_label = QLabel(_('Title to test when debugging:')) self.debugging_title_edit = QLineEdit(self) self.debugging_title_edit.setToolTip(_('Part of title of a book that can be used when doing some tests for debugging. ' 'The test is to see if the string is contained in the title of a book. ' 'The better the match, the less extraneous output.')) self.debugging_title_edit.setText(device.get_pref('debugging_title')) self.debugging_title_label.setBuddy(self.debugging_title_edit) self.options_layout.addWidget(self.support_newer_firmware_checkbox, 0, 0, 1, 2) self.options_layout.addWidget(self.debugging_title_label, 1, 0, 1, 1) self.options_layout.addWidget(self.debugging_title_edit, 1, 1, 1, 1) self.options_layout.setRowStretch(2, 2) @property def support_newer_firmware(self): return self.support_newer_firmware_checkbox.isChecked() @property def debugging_title(self): return self.debugging_title_edit.text().strip()
def _initUi(self): self._paramWidget = DyStockSelectStrategyParamWidget() self._paramWidget.set(self._param) self._periodsResultWidget = DyStockBackTestingStrategyPeriodsResultWidget(self._strategyCls, self._paramGroupNo, self._param, self._eventEngine, self._dataEngine, self._dataViewer) grid = QGridLayout() grid.setSpacing(0) grid.addWidget(self._paramWidget, 0, 0) grid.addWidget(self._periodsResultWidget, 1, 0) grid.setRowStretch(0, 1) grid.setRowStretch(1, 20) self.setLayout(grid)
class DeviceListGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(DeviceListGroupBox, self).__init__(parent, device) self.setTitle(_("Show as on device")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.show_recommendations_checkbox = create_checkbox( _("Show recommendations"), _('Kobo shows recommendations on the device. In some cases these have ' 'files but in other cases they are just pointers to the web site to buy. ' 'Enable if you wish to see/delete them.'), device.get_pref('show_recommendations')) self.show_archived_books_checkbox = create_checkbox( _("Show archived books"), _('Archived books are listed on the device but need to be downloaded to read.' ' Use this option to show these books and match them with books in the calibre library.' ), device.get_pref('show_archived_books')) self.show_previews_checkbox = create_checkbox( _('Show previews'), _('Kobo previews are included on the Touch and some other versions' ' by default they are no longer displayed as there is no good reason to ' 'see them. Enable if you wish to see/delete them.'), device.get_pref('show_previews')) self.options_layout.addWidget(self.show_recommendations_checkbox, 0, 0, 1, 1) self.options_layout.addWidget(self.show_archived_books_checkbox, 1, 0, 1, 1) self.options_layout.addWidget(self.show_previews_checkbox, 2, 0, 1, 1) self.options_layout.setRowStretch(3, 1) @property def show_recommendations(self): return self.show_recommendations_checkbox.isChecked() @property def show_archived_books(self): return self.show_archived_books_checkbox.isChecked() @property def show_previews(self): return self.show_previews_checkbox.isChecked()
def _initUi(self): self._paramWidget = DyStockSelectStrategyParamWidget() self._paramWidget.set(self._param) self._periodsResultWidget = DyStockBackTestingStrategyPeriodsResultWidget( self._strategyCls, self._paramGroupNo, self._param, self._eventEngine, self._dataEngine, self._dataViewer) grid = QGridLayout() grid.setSpacing(0) grid.addWidget(self._paramWidget, 0, 0) grid.addWidget(self._periodsResultWidget, 1, 0) grid.setRowStretch(0, 1) grid.setRowStretch(1, 20) self.setLayout(grid)
class CoversGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(CoversGroupBox, self).__init__(parent, device) self.setTitle(_("Upload covers")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.setCheckable(True) self.setChecked(device.get_pref('upload_covers')) self.setToolTip( wrap_msg( _('Upload cover images from the calibre library when sending books to the device.' ))) self.upload_grayscale_checkbox = create_checkbox( _('Upload black and white Covers'), _('Convert covers to black and white when uploading'), device.get_pref('upload_grayscale')) self.keep_cover_aspect_checkbox = create_checkbox( _('Keep cover aspect ratio'), _('When uploading covers, do not change the aspect ratio when resizing for the device.' ' This is for firmware versions 2.3.1 and later.'), device.get_pref('keep_cover_aspect')) self.options_layout.addWidget(self.keep_cover_aspect_checkbox, 0, 0, 1, 1) self.options_layout.addWidget(self.upload_grayscale_checkbox, 1, 0, 1, 1) self.options_layout.setRowStretch(2, 1) @property def upload_covers(self): return self.isChecked() @property def upload_grayscale(self): return self.upload_grayscale_checkbox.isChecked() @property def keep_cover_aspect(self): return self.keep_cover_aspect_checkbox.isChecked()
class CoversGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(CoversGroupBox, self).__init__(parent, device) self.setTitle(_("Upload covers")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.setCheckable(True) self.setChecked(device.get_pref('upload_covers')) self.setToolTip(wrap_msg(_('Upload cover images from the calibre library when sending books to the device.'))) self.upload_grayscale_checkbox = create_checkbox( _('Upload Black and White Covers'), _('Convert covers to Black and White when uploading'), device.get_pref('upload_grayscale') ) self.keep_cover_aspect_checkbox = create_checkbox( _('Keep cover aspect ratio'), _('When uploading covers, do not change the aspect ratio when resizing for the device.' ' This is for firmware versions 2.3.1 and later.'), device.get_pref('keep_cover_aspect')) self.options_layout.addWidget(self.keep_cover_aspect_checkbox, 0, 0, 1, 1) self.options_layout.addWidget(self.upload_grayscale_checkbox, 1, 0, 1, 1) self.options_layout.setRowStretch(2, 1) @property def upload_covers(self): return self.isChecked() @property def upload_grayscale(self): return self.upload_grayscale_checkbox.isChecked() @property def keep_cover_aspect(self): return self.keep_cover_aspect_checkbox.isChecked()
class BookUploadsGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(BookUploadsGroupBox, self).__init__(parent, device) self.setTitle(_("Book uploading")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.modify_css_checkbox = create_checkbox( _("Modify CSS"), _('This allows addition of user CSS rules and removal of some CSS. ' 'When sending a book, the driver adds the contents of {0} to all stylesheets in the EPUB. ' 'This file is searched for in the root directory of the main memory of the device. ' 'As well as this, if the file contains settings for the "orphans" or "widows", ' 'these are removed for all styles in the original stylesheet.').format(device.KOBO_EXTRA_CSSFILE), device.get_pref('modify_css') ) self.override_kobo_replace_existing_checkbox = create_checkbox( _("Do not treat replacements as new books"), _('When a new book is side-loaded, the Kobo firmware imports details of the book into the internal database. ' 'Even if the book is a replacement for an existing book, the Kobo will remove the book from the database and then treat it as a new book. ' 'This means that the reading status, bookmarks and collections for the book will be lost. ' 'This option overrides firmware behavior and attempts to prevent a book that has been resent from being treated as a new book. ' 'If you prefer to have replacements treated as new books, turn this option off.' ), device.get_pref('override_kobo_replace_existing') ) self.options_layout.addWidget(self.modify_css_checkbox, 0, 0, 1, 2) self.options_layout.addWidget(self.override_kobo_replace_existing_checkbox, 1, 0, 1, 2) self.options_layout.setRowStretch(2, 1) @property def modify_css(self): return self.modify_css_checkbox.isChecked() @property def override_kobo_replace_existing(self): return self.override_kobo_replace_existing_checkbox.isChecked()
class BookUploadsGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(BookUploadsGroupBox, self).__init__(parent, device) self.setTitle(_("Book uploading")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.modify_css_checkbox = create_checkbox( _("Modify CSS"), _('This allows addition of user CSS rules and removal of some CSS. ' 'When sending a book, the driver adds the contents of {0} to all stylesheets in the EPUB. ' 'This file is searched for in the root directory of the main memory of the device. ' 'As well as this, if the file contains settings for the "orphans" or "widows", ' 'these are removed for all styles in the original stylesheet.'). format(device.KOBO_EXTRA_CSSFILE), device.get_pref('modify_css')) self.override_kobo_replace_existing_checkbox = create_checkbox( _("Do not treat replacements as new books"), _('When a new book is side-loaded, the Kobo firmware imports details of the book into the internal database. ' 'Even if the book is a replacement for an existing book, the Kobo will remove the book from the database and then treat it as a new book. ' 'This means that the reading status, bookmarks and collections for the book will be lost. ' 'This option overrides firmware behavior and attempts to prevent a book that has been resent from being treated as a new book. ' 'If you prefer to have replacements treated as new books, turn this option off.' ), device.get_pref('override_kobo_replace_existing')) self.options_layout.addWidget(self.modify_css_checkbox, 0, 0, 1, 2) self.options_layout.addWidget( self.override_kobo_replace_existing_checkbox, 1, 0, 1, 2) self.options_layout.setRowStretch(2, 1) @property def modify_css(self): return self.modify_css_checkbox.isChecked() @property def override_kobo_replace_existing(self): return self.override_kobo_replace_existing_checkbox.isChecked()
class BookUploadsGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(BookUploadsGroupBox, self).__init__(parent, device) self.setTitle(_("Book uploading")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.modify_css_checkbox = create_checkbox( _("Modify CSS"), _('This allows addition of user CSS rules and removal of some CSS. ' 'When sending a book, the driver adds the contents of {0} to all stylesheets in the EPUB. ' 'This file is searched for in the root directory of the main memory of the device. ' 'As well as this, if the file contains settings for the "orphans" or "widows", ' 'these are removed for all styles in the original stylesheet.'). format(device.KOBO_EXTRA_CSSFILE), device.get_pref('modify_css')) self.options_layout.addWidget(self.modify_css_checkbox, 0, 0, 1, 2) self.options_layout.setRowStretch(1, 1) @property def modify_css(self): return self.modify_css_checkbox.isChecked()
class ExtendedGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(ExtendedGroupBox, self).__init__(parent, device, _("Extended driver")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.extra_features_checkbox = create_checkbox( _('Enable Extended Kobo Features'), _('Choose whether to enable extra customizations'), device.get_pref('extra_features')) self.upload_encumbered_checkbox = create_checkbox( _('Upload DRM-encumbered ePub files'), _('Select this to upload ePub files encumbered by DRM. If this is ' 'not selected, it is a fatal error to upload an encumbered file'), device.get_pref('upload_encumbered')) self.skip_failed_checkbox = create_checkbox( _('Silently Ignore Failed Conversions'), _('Select this to not upload any book that fails conversion to ' 'kepub. If this is not selected, the upload process will be ' 'stopped at the first book that fails. If this is selected, ' 'failed books will be silently removed from the upload queue.'), device.get_pref('skip_failed')) self.hyphenate_checkbox = create_checkbox( _('Hyphenate Files'), _('Select this to add a CSS file which enables hyphenation. The ' 'language used will be the language defined for the book in ' 'calibre. Please see the README file for directions on updating ' 'hyphenation dictionaries.'), device.get_pref('hyphenate')) self.replace_lang_checkbox = create_checkbox( _('Replace Content Language Code'), _('Select this to replace the defined language in each content ' 'file inside the ePub.'), device.get_pref('replace_lang')) self.smarten_punctuation_checkbox = create_checkbox( _('Smarten Punctuation'), _('Select this to smarten punctuation in the ePub'), device.get_pref('smarten_punctuation')) self.clean_markup_checkbox = create_checkbox( _('Clean up ePub Markup'), _('Select this to clean up the internal ePub markup.'), device.get_pref('clean_markup')) self.file_copy_dir_checkbox = create_checkbox( _('Copy generated KePub files to a directory'), _('Enter an absolute directory path to copy all generated KePub ' 'files into for debugging purposes.'), device.get_pref('file_copy_dir')) self.file_copy_dir_label = QLabel(_( 'Copy generated KePub files to a directory')) self.file_copy_dir_edit = QLineEdit(self) self.file_copy_dir_edit.setToolTip( _('Enter an absolute directory path to copy all generated KePub ' 'files into for debugging purposes.')) self.file_copy_dir_edit.setText(device.get_pref('file_copy_dir')) self.file_copy_dir_label.setBuddy(self.file_copy_dir_edit) self.full_page_numbers_checkbox = create_checkbox( _('Use full book page numbers'), _('Select this to show page numbers for the whole book, instead of ' 'each chapter. This will also affect regular ePub page number ' 'display!'), device.get_pref('full_page_numbers')) self.disable_hyphenation_checkbox = create_checkbox( _('Disable hyphenation'), _('Select this to disable hyphenation for books.'), device.get_pref('disable_hyphenation')) self.options_layout.addWidget(self.extra_features_checkbox, 0, 0, 1, 1) self.options_layout.addWidget(self.upload_encumbered_checkbox, 0, 1, 1, 1) self.options_layout.addWidget(self.skip_failed_checkbox, 1, 0, 1, 1) self.options_layout.addWidget(self.hyphenate_checkbox, 1, 1, 1, 1) self.options_layout.addWidget(self.replace_lang_checkbox, 2, 0, 1, 1) self.options_layout.addWidget(self.smarten_punctuation_checkbox, 2, 1, 1, 1) self.options_layout.addWidget(self.clean_markup_checkbox, 3, 0, 1, 1) self.options_layout.addWidget(self.file_copy_dir_label, 4, 0, 1, 1) self.options_layout.addWidget(self.file_copy_dir_edit, 4, 1, 1, 1) self.options_layout.addWidget(self.full_page_numbers_checkbox, 5, 0, 1, 1) self.options_layout.addWidget(self.disable_hyphenation_checkbox, 5, 1, 1, 1) self.options_layout.setRowStretch(6, 2) @property def extra_features(self): return self.extra_features_checkbox.isChecked() @property def upload_encumbered(self): return self.upload_encumbered_checkbox.isChecked() @property def skip_failed(self): return self.skip_failed_checkbox.isChecked() @property def hyphenate(self): return self.hyphenate_checkbox.isChecked() @property def replace_lang(self): return self.replace_lang_checkbox.isChecked() @property def smarten_punctuation(self): return self.smarten_punctuation_checkbox.isChecked() @property def clean_markup(self): return self.clean_markup_checkbox.isChecked() @property def full_page_numbers(self): return self.full_page_numbers_checkbox.isChecked() @property def disable_hyphenation(self): return self.disable_hyphenation_checkbox.isChecked() @property def file_copy_dir(self): return self.file_copy_dir_edit.text().strip()
class PrincePDFDialog(QDialog): prince_log = '' # GUI definition def __init__(self, gui, icon, do_user_config): QDialog.__init__(self, gui) self.icon = icon self.gui = gui self.do_user_config = do_user_config self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowTitle(_('Prince PDF')) self.setWindowIcon(icon) self.l = QGridLayout() self.setLayout(self.l) self.image = QLabel() self.image.setPixmap(icon.pixmap(120, 120)) self.l.addWidget(self.image, 1, 1, 4, 1, Qt.AlignVCenter) self.convert_to_PDF_button = QPushButton(_('Convert to &PDF'), self) self.convert_to_PDF_button.clicked.connect(self.convert_to_PDF) self.convert_to_PDF_button.setDefault(True) self.convert_to_PDF_button.setToolTip( _('<qt>Start the conversion process</qt>')) self.l.addWidget(self.convert_to_PDF_button, 1, 2, Qt.AlignVCenter) self.view_log = QPushButton(_('&View log'), self) self.view_log.clicked.connect(self.show_log) self.view_log.setToolTip( _('<qt>Display the log from the last Prince run</qt>')) self.l.addWidget(self.view_log, 2, 2, Qt.AlignVCenter) self.view_log.hide() self.conf_button = QPushButton(_('Con&figure'), self) self.conf_button.clicked.connect(self.config) self.conf_button.setToolTip(_('<qt>Configure this plugin</qt>')) self.l.addWidget(self.conf_button, 4, 2, Qt.AlignVCenter) self.info = QLabel() self.l.addWidget(self.info, 5, 1, 1, -1) self.suggestion = QLabel() self.suggestion.setAlignment(Qt.AlignCenter) self.l.addWidget(self.suggestion, 6, 1, 1, -1) self.l.setColumnStretch(1, 1) self.l.setColumnStretch(2, 10) self.l.setRowStretch(1, 1) self.l.setRowStretch(2, 1) self.l.setRowStretch(3, 10) self.l.setRowStretch(4, 1) self.l.setRowStretch(5, 1) self.l.setRowStretch(6, 1) self.l.setRowMinimumHeight(1, 1) self.l.setRowMinimumHeight(2, 1) self.l.setRowMinimumHeight(3, 1) self.l.setRowMinimumHeight(4, 1) self.l.setRowMinimumHeight(5, 1) self.l.setRowMinimumHeight(6, 1) self.buttons = QDialogButtonBox(QDialogButtonBox.Close | QDialogButtonBox.Help) self.l.addWidget(self.buttons, 7, 1, 1, -1) self.buttons.rejected.connect(self.reject) self.buttons.helpRequested.connect(self.about) self.adjustSize() # Update the info now and every time the selection or the data changes self.gui.library_view.model().dataChanged.connect(self.update_info) self.gui.library_view.selectionModel().selectionChanged.connect( self.update_info) self.update_info() def update_info(self): ''' Update the info on top of the window ''' self.db = self.gui.current_db # Get selected rows rows = self.gui.library_view.selectionModel().selectedRows() if not rows or len(rows) == 0: self.info.setText(_('<b>No books selected</b>')) self.info.setAlignment(Qt.AlignCenter) self.suggestion.setText(_('Select one single book')) self.selected = None self.convert_to_PDF_button.setEnabled(False) elif len(rows) > 1: self.info.setText(_('<b>Many books selected</b>')) self.info.setAlignment(Qt.AlignCenter) self.suggestion.setText(_('Select one single book')) self.selected = None self.convert_to_PDF_button.setEnabled(False) else: # If there is only one selected book, enable conversion # and show list of available formats (formats in prefs are bold) book_id = self.gui.library_view.model().id(rows[0]) fmts = self.db.formats(book_id, index_is_id=True) if (fmts): fmts = fmts.split(',') else: fmts = [_('<i>none</i>')] available = False for i, fmt in enumerate(fmts): fmts[i] = fmt.lower() if fmts[i] in prefs['formats']: fmts[i] = '<b>%s</b>' % fmts[i] available = True self.info.setText(_('Available formats: %s') % ', '.join(fmts)) self.info.setAlignment(Qt.AlignLeft) # Conversion enabled only if some "preferred" format is found if (available): self.suggestion.setText(_('Ready')) self.selected = book_id self.convert_to_PDF_button.setEnabled(True) else: self.suggestion.setText(_('No preferred format available')) self.selected = None self.convert_to_PDF_button.setEnabled(False) def about(self): ''' Display a short help message ''' from os.path import join from calibre.ptempfile import TemporaryDirectory from calibre.gui2.dialogs.message_box import MessageBox from calibre_plugins.prince_pdf.help import help_txt, license_txt from calibre_plugins.prince_pdf import PrincePDFPlugin from calibre_plugins.prince_pdf import __license__ author = PrincePDFPlugin.author version = "%i.%i.%i" % PrincePDFPlugin.version license = __license__ with TemporaryDirectory('xxx') as tdir: for x in ('prince_icon.png', 'small_icon.png'): with open(join(tdir, x), 'w') as f: f.write(get_resources('images/' + x)) help_box = MessageBox(type_ = MessageBox.INFO, \ title = _('About the Prince PDF Plugin'), \ msg = help_txt % {'author':author, 'version':version, 'license':license, 'dir':tdir, 'code':'style="font-family:monospace ; font-weight:bold"'}, \ det_msg = 'Copyright \u00a9 %s\n%s' % (__copyright__, license_txt), \ q_icon = self.icon, \ show_copy_button = False) #help_box.gridLayout.addWidget(help_box.icon_widget,0,0,Qt.AlignTop) help_box.gridLayout.setAlignment(help_box.icon_widget, Qt.AlignTop) help_box.exec_() def convert_to_PDF(self): ''' Unpack and convert the currently selected book to PDF ''' from calibre.gui2 import error_dialog from calibre.constants import DEBUG # Get available formats book_id = self.selected fmts = self.db.formats(book_id, index_is_id=True) fmts = fmts.lower().split(',') # Process only the first format matching the 'formats' configuration option for fmt in prefs['formats']: fmt = fmt.lower() if (not fmt in fmts): continue mi = self.db.get_metadata(book_id, index_is_id=True, get_cover=False) pdf_base_file = self.get_filename(book_id, mi) # This is the actual code: if DEBUG: print('===========') # Unpack the book and call the conversion dialog (opf, oeb) = self.unpack(book_id, fmt) if (opf == None or oeb == None): return error_dialog(self.gui, _('Cannot convert to PDF'), _('Format not supported: %s') % fmt, show=True) convert_dialog = ConvertDialog(mi, fmt, opf, oeb, self.icon) convert_dialog.pdf_file = pdf_base_file pdf_file = '' if (convert_dialog.exec_()): pdf_file = convert_dialog.pdf_file self.prince_log = convert_dialog.prince_log # After the dialog returns, pdf_file has the output file path, # and prince_log has the Prince console output if DEBUG: print(_('PDF file: %s') % pdf_file) # If there is any log, enable the View log button if (self.prince_log): self.view_log.show() log_msg = _(' Check the log.') else: self.view_log.hide() log_msg = '' # If the conversion failed, pdf_file will be None, if (pdf_file == None): error_dialog(self.gui, _('Could not convert to PDF'), _('The conversion failed.') + log_msg, show=True) # If the user cancelled the dialog, pdf_file will be '' if (pdf_file): # Set the metadata in the PDF and add it or save it try: self.set_pdf_metadata(mi, pdf_file) except: error_dialog( self.gui, _('Could not convert to PDF'), _("Error reading or writing the PDF file:\n%s" % pdf_file), show=True) return if (prefs['add_book']): self.add_pdf(book_id, pdf_file, ('pdf' in fmts)) else: self.save_pdf(pdf_file, pdf_base_file) if DEBUG: print('===========') return # No matching format in the book return error_dialog(self.gui, _('Cannot convert to PDF'), _('No supported format available'), show=True) def show_log(self): ''' Display the Prince log dialog ''' msg = LogDialog(self.prince_log, self.icon) msg.exec_() def config(self): ''' Display the configuration dialog ''' self.do_user_config(parent=self) def get_filename(self, book_id, mi): ''' Obtain a filename from the save_to_disk template :param book_id: The book identifier :param mi: The book metadata ''' from os.path import join from calibre.library.save_to_disk import get_components, config from calibre import sanitize_file_name_unicode from calibre.utils.filenames import ascii_filename opts = config().parse() components = get_components( opts.template, mi, book_id, opts.timefmt, sanitize_func=(ascii_filename if opts.asciiize else sanitize_file_name_unicode), to_lowercase=opts.to_lowercase, replace_whitespace=opts.replace_whitespace) base_path = join(*components) return '%s.pdf' % base_path def unpack(self, book_id, fmt): ''' Unpack the book in a temporary directory :param book_id: The book identifier :param fmt: The format to unpack ''' from calibre.constants import DEBUG from calibre.ptempfile import PersistentTemporaryDirectory from calibre.ebooks.tweak import get_tools from calibre.ebooks.oeb.base import OEBBook from calibre.ebooks.oeb.reader import OEBReader from calibre.utils.logging import default_log from calibre_plugins.prince_pdf.dummy_preprocessor import dummy_preprocessor book_file = self.db.format(book_id, fmt, index_is_id=True, as_path=True) if DEBUG: print(_('Unpacking book...')) tdir = PersistentTemporaryDirectory('_unpack') exploder = get_tools(fmt)[0] if (exploder == None): return (None, None) opf = exploder(book_file, tdir) html_preprocessor = dummy_preprocessor() css_preprocessor = dummy_preprocessor() oeb = OEBBook(default_log, html_preprocessor, css_preprocessor) OEBReader()(oeb, opf) return (opf, oeb) def set_pdf_metadata(self, mi, pdf_file): ''' Set the metadata in the PDF file :param mi: The book metadata :param pdf_file: The path to the PDF file ''' from calibre.constants import DEBUG from calibre.ebooks.metadata.pdf import set_metadata if DEBUG: print(_('Setting metadata...')) pdf_stream = open(pdf_file, 'r+b') set_metadata(pdf_stream, mi) pdf_stream.close() def add_pdf(self, book_id, pdf_file, exists): ''' Add the PDF file to the book record, asking for replacement :param book_id: The book identifier :param pdf_file: The path to the PDF file :param exists: True if there is already a PDF in the book ''' from calibre.constants import DEBUG from calibre.gui2.dialogs.message_box import MessageBox add_it = True if (exists): msg = MessageBox( MessageBox.QUESTION, _('Existing format'), _('The selected book already contains a PDF format. Are you sure you want to replace it?' ), _("The temporary file can be found in:\n%s") % pdf_file) msg.toggle_det_msg() add_it = (msg.exec_()) if (add_it): if DEBUG: print(_('Adding PDF...')) self.db.new_api.add_format(book_id, 'pdf', pdf_file) self.gui.library_view.model().refresh_ids([book_id]) self.gui.library_view.refresh_book_details() self.gui.tags_view.recount() def save_pdf(self, pdf_file, pdf_base_file): ''' Save the PDF file in the final location :param pdf_file: The path to the PDF file :param pdf_base_file: The desired file name and relative path ''' from os import makedirs from os.path import basename, dirname, join, exists from shutil import move from calibre.constants import DEBUG from calibre.gui2 import choose_dir from calibre.gui2.dialogs.message_box import MessageBox from calibre.gui2 import error_dialog path = choose_dir(self.gui, 'save to disk dialog', _('Choose destination directory')) if not path: return save_file = join(path, pdf_base_file) base_dir = dirname(save_file) try: makedirs(base_dir) except BaseException: if not exists(base_dir): raise try: move(pdf_file, save_file) except: error_dialog(self.gui, _('Could not save PDF'), _("Error writing the PDF file:\n%s" % save_file), show=True) return if DEBUG: print(save_file) MessageBox(MessageBox.INFO, _('File saved'), _("PDF file saved in:\n%s") % save_file).exec_()
class ExtendedGroupBox(DeviceOptionsGroupBox): """The options group for KoboTouchExtended.""" def __init__(self, parent, device): """Set up driver config options group.""" super(ExtendedGroupBox, self).__init__( parent, device, _("Extended driver") # noqa: F821 ) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.extra_features_checkbox = create_checkbox( _("Enable Extended Kobo Features"), # noqa: F821 _("Choose whether to enable extra customizations"), # noqa: F821 device.get_pref("extra_features"), ) self.upload_encumbered_checkbox = create_checkbox( _("Upload DRM-encumbered ePub files"), # noqa: F821 _( # noqa: F821 "Select this to upload ePub files encumbered by DRM. If this " "is not selected, it is a fatal error to upload an encumbered " "file" ), device.get_pref("upload_encumbered"), ) self.skip_failed_checkbox = create_checkbox( _("Silently Ignore Failed Conversions"), # noqa: F821 _( # noqa: F821 "Select this to not upload any book that fails conversion to " "kepub. If this is not selected, the upload process will be " "stopped at the first book that fails. If this is selected, " "failed books will be silently removed from the upload queue." ), device.get_pref("skip_failed"), ) self.hyphenate_checkbox = create_checkbox( _("Hyphenate Files"), # noqa: F821 _( # noqa: F821 "Select this to add a CSS file which enables hyphenation. The " "language used will be the language defined for the book in " "calibre. Please see the README file for directions on " "updating hyphenation dictionaries." ), device.get_pref("hyphenate"), ) self.smarten_punctuation_checkbox = create_checkbox( _("Smarten Punctuation"), # noqa: F821 _("Select this to smarten punctuation in the ePub"), # noqa: F821 device.get_pref("smarten_punctuation"), ) self.clean_markup_checkbox = create_checkbox( _("Clean up ePub Markup"), # noqa: F821 _("Select this to clean up the internal ePub markup."), # noqa: F821 device.get_pref("clean_markup"), ) self.file_copy_dir_checkbox = create_checkbox( _("Copy generated KePub files to a directory"), # noqa: F821 _( # noqa: F821 "Enter an absolute directory path to copy all generated KePub " "files into for debugging purposes." ), device.get_pref("file_copy_dir"), ) self.file_copy_dir_label = QLabel( _("Copy generated KePub files to a directory") # noqa: F821 ) self.file_copy_dir_edit = QLineEdit(self) self.file_copy_dir_edit.setToolTip( _( # noqa: F821 "Enter an absolute directory path to copy all generated KePub " "files into for debugging purposes." ) ) self.file_copy_dir_edit.setText(device.get_pref("file_copy_dir")) self.file_copy_dir_label.setBuddy(self.file_copy_dir_edit) self.full_page_numbers_checkbox = create_checkbox( _("Use full book page numbers"), # noqa: F821 _( # noqa: F821 "Select this to show page numbers for the whole book, instead " "of each chapter. This will also affect regular ePub page " "number display!" ), device.get_pref("full_page_numbers"), ) self.disable_hyphenation_checkbox = create_checkbox( _("Disable hyphenation"), # noqa: F821 _("Select this to disable hyphenation for books."), # noqa: F821 device.get_pref("disable_hyphenation"), ) self.options_layout.addWidget(self.extra_features_checkbox, 0, 0, 1, 1) self.options_layout.addWidget(self.upload_encumbered_checkbox, 0, 1, 1, 1) self.options_layout.addWidget(self.skip_failed_checkbox, 1, 0, 1, 1) self.options_layout.addWidget(self.hyphenate_checkbox, 1, 1, 1, 1) self.options_layout.addWidget(self.smarten_punctuation_checkbox, 2, 1, 1, 1) self.options_layout.addWidget(self.clean_markup_checkbox, 3, 0, 1, 1) self.options_layout.addWidget(self.file_copy_dir_label, 4, 0, 1, 1) self.options_layout.addWidget(self.file_copy_dir_edit, 4, 1, 1, 1) self.options_layout.addWidget(self.full_page_numbers_checkbox, 5, 0, 1, 1) self.options_layout.addWidget(self.disable_hyphenation_checkbox, 5, 1, 1, 1) self.options_layout.setRowStretch(6, 2) @property def extra_features(self): """Determine if Kobo extra features are enabled.""" return self.extra_features_checkbox.isChecked() @property def upload_encumbered(self): """Determine if DRM-encumbered files will be uploaded.""" return self.upload_encumbered_checkbox.isChecked() @property def skip_failed(self): """Determine if failed conversions will be skipped.""" return self.skip_failed_checkbox.isChecked() @property def hyphenate(self): """Determine if hyphenation should be enabled.""" return self.hyphenate_checkbox.isChecked() @property def smarten_punctuation(self): """Determine if punctuation should be converted to smart punctuation.""" return self.smarten_punctuation_checkbox.isChecked() @property def clean_markup(self): """Determine if additional markup cleanup will be done.""" return self.clean_markup_checkbox.isChecked() @property def full_page_numbers(self): """Determine if full-book page numbers will be displayed.""" return self.full_page_numbers_checkbox.isChecked() @property def disable_hyphenation(self): """Determine if hyphenation should be disabled.""" return self.disable_hyphenation_checkbox.isChecked() @property def file_copy_dir(self): """Determine where to copy converted KePub books to.""" return self.file_copy_dir_edit.text().strip()
def _initUi(self): self._statsWidget = DyStockBackTestingStrategyResultStatsWidget(self._dataEngine) self._posWidget = DyStockBackTestingStrategyResultPositionWidget(self._dataViewer) self._dealsWidget = DyStockBackTestingStrategyResultDealsWidget(self._eventEngine, str(self._period), self._strategyCls) self._subInfoWidget = DySubInfoWidget(self._eventEngine, self._paramGroupNo, self._period) # 由于线程并行问题,可能会丢掉开始的一些信息 dealsLabel = QLabel('成交明细') posLabel = QLabel('当前持仓') statsLabel = QLabel('账户信息') grid = QGridLayout() grid.setSpacing(0) grid.addWidget(statsLabel, 0, 0) grid.addWidget(self._statsWidget, 1, 0) grid.addWidget(posLabel, 2, 0) grid.addWidget(self._posWidget, 3, 0) grid.addWidget(dealsLabel, 4, 0) grid.addWidget(self._dealsWidget, 5, 0) grid.addWidget(self._subInfoWidget, 6, 0) grid.setRowStretch(0, 1) grid.setRowStretch(1, 4) grid.setRowStretch(2, 1) grid.setRowStretch(3, 25) grid.setRowStretch(4, 1) grid.setRowStretch(5, 30) grid.setRowStretch(6, 1) self.setLayout(grid) # 成交明细右键菜单 self._dealsNewWindows = [] dealsLabel.setContextMenuPolicy(Qt.CustomContextMenu) dealsLabel.customContextMenuRequested.connect(self._showDealsContextMenu) self._dealsMenu = QMenu(self) action = QAction('新窗口', self) action.triggered.connect(self._newDealsWindow) self._dealsMenu.addAction(action)
class ExtendedGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(ExtendedGroupBox, self).__init__(parent, device, _("Extended driver")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.extra_features_checkbox = create_checkbox( _('Enable Extended Kobo Features'), _('Choose whether to enable extra customizations'), device.get_pref('extra_features')) self.upload_encumbered_checkbox = create_checkbox( _('Upload DRM-encumbered ePub files'), _('Select this to upload ePub files encumbered by DRM. If this is ' 'not selected, it is a fatal error to upload an encumbered file' ), device.get_pref('upload_encumbered')) self.skip_failed_checkbox = create_checkbox( _('Silently Ignore Failed Conversions'), _('Select this to not upload any book that fails conversion to ' 'kepub. If this is not selected, the upload process will be ' 'stopped at the first book that fails. If this is selected, ' 'failed books will be silently removed from the upload queue.'), device.get_pref('skip_failed')) self.hyphenate_checkbox = create_checkbox( _('Hyphenate Files'), _('Select this to add a CSS file which enables hyphenation. The ' 'language used will be the language defined for the book in ' 'calibre. Please see the README file for directions on updating ' 'hyphenation dictionaries.'), device.get_pref('hyphenate')) self.replace_lang_checkbox = create_checkbox( _('Replace Content Language Code'), _('Select this to replace the defined language in each content ' 'file inside the ePub.'), device.get_pref('replace_lang')) self.smarten_punctuation_checkbox = create_checkbox( _('Smarten Punctuation'), _('Select this to smarten punctuation in the ePub'), device.get_pref('smarten_punctuation')) self.clean_markup_checkbox = create_checkbox( _('Clean up ePub Markup'), _('Select this to clean up the internal ePub markup.'), device.get_pref('clean_markup')) self.file_copy_dir_checkbox = create_checkbox( _('Copy generated KePub files to a directory'), _('Enter an absolute directory path to copy all generated KePub ' 'files into for debugging purposes.'), device.get_pref('file_copy_dir')) self.file_copy_dir_label = QLabel( _('Copy generated KePub files to a directory')) self.file_copy_dir_edit = QLineEdit(self) self.file_copy_dir_edit.setToolTip( _('Enter an absolute directory path to copy all generated KePub ' 'files into for debugging purposes.')) self.file_copy_dir_edit.setText(device.get_pref('file_copy_dir')) self.file_copy_dir_label.setBuddy(self.file_copy_dir_edit) self.full_page_numbers_checkbox = create_checkbox( _('Use full book page numbers'), _('Select this to show page numbers for the whole book, instead of ' 'each chapter. This will also affect regular ePub page number ' 'display!'), device.get_pref('full_page_numbers')) self.disable_hyphenation_checkbox = create_checkbox( _('Disable hyphenation'), _('Select this to disable hyphenation for books.'), device.get_pref('disable_hyphenation')) self.options_layout.addWidget(self.extra_features_checkbox, 0, 0, 1, 1) self.options_layout.addWidget(self.upload_encumbered_checkbox, 0, 1, 1, 1) self.options_layout.addWidget(self.skip_failed_checkbox, 1, 0, 1, 1) self.options_layout.addWidget(self.hyphenate_checkbox, 1, 1, 1, 1) self.options_layout.addWidget(self.replace_lang_checkbox, 2, 0, 1, 1) self.options_layout.addWidget(self.smarten_punctuation_checkbox, 2, 1, 1, 1) self.options_layout.addWidget(self.clean_markup_checkbox, 3, 0, 1, 1) self.options_layout.addWidget(self.file_copy_dir_label, 4, 0, 1, 1) self.options_layout.addWidget(self.file_copy_dir_edit, 4, 1, 1, 1) self.options_layout.addWidget(self.full_page_numbers_checkbox, 5, 0, 1, 1) self.options_layout.addWidget(self.disable_hyphenation_checkbox, 5, 1, 1, 1) self.options_layout.setRowStretch(6, 2) @property def extra_features(self): return self.extra_features_checkbox.isChecked() @property def upload_encumbered(self): return self.upload_encumbered_checkbox.isChecked() @property def skip_failed(self): return self.skip_failed_checkbox.isChecked() @property def hyphenate(self): return self.hyphenate_checkbox.isChecked() @property def replace_lang(self): return self.replace_lang_checkbox.isChecked() @property def smarten_punctuation(self): return self.smarten_punctuation_checkbox.isChecked() @property def clean_markup(self): return self.clean_markup_checkbox.isChecked() @property def full_page_numbers(self): return self.full_page_numbers_checkbox.isChecked() @property def disable_hyphenation(self): return self.disable_hyphenation_checkbox.isChecked() @property def file_copy_dir(self): return self.file_copy_dir_edit.text().strip()
def __init__(self, gui, existing_names, editing=None): QDialog.__init__(self, gui) self.gui = gui self.existing_names = existing_names if editing: self.setWindowTitle(_('Edit virtual library')) else: self.setWindowTitle(_('Create virtual library')) self.setWindowIcon(QIcon(I('lt.png'))) gl = QGridLayout() self.setLayout(gl) self.la1 = la1 = QLabel(_('Virtual library &name:')) gl.addWidget(la1, 0, 0) self.vl_name = QComboBox() self.vl_name.setEditable(True) self.vl_name.lineEdit().setMaxLength(MAX_VIRTUAL_LIBRARY_NAME_LENGTH) la1.setBuddy(self.vl_name) gl.addWidget(self.vl_name, 0, 1) self.editing = editing self.saved_searches_label = sl = QTextBrowser(self) sl.viewport().setAutoFillBackground(False) gl.addWidget(sl, 2, 0, 1, 2) self.la2 = la2 = QLabel(_('&Search expression:')) gl.addWidget(la2, 1, 0) self.vl_text = QLineEdit() self.vl_text.textChanged.connect(self.search_text_changed) la2.setBuddy(self.vl_text) gl.addWidget(self.vl_text, 1, 1) self.vl_text.setText(_build_full_search_string(self.gui)) self.sl = sl = QLabel( '<p>' + _('Create a virtual library based on: ') + ('<a href="author.{0}">{0}</a>, ' '<a href="tag.{1}">{1}</a>, ' '<a href="publisher.{2}">{2}</a>, ' '<a href="series.{3}">{3}</a>, ' '<a href="search.{4}">{4}</a>.').format(_('Authors'), _( 'Tags'), _('Publishers'), _('Series'), _('Saved searches'))) sl.setWordWrap(True) sl.setTextInteractionFlags(Qt.LinksAccessibleByMouse) sl.linkActivated.connect(self.link_activated) gl.addWidget(sl, 3, 0, 1, 2) gl.setRowStretch(3, 10) self.hl = hl = QLabel( _(''' <h2>Virtual libraries</h2> <p>Using <i>virtual libraries</i> you can restrict calibre to only show you books that match a search. When a virtual library is in effect, calibre behaves as though the library contains only the matched books. The Tag browser display only the tags/authors/series/etc. that belong to the matched books and any searches you do will only search within the books in the virtual library. This is a good way to partition your large library into smaller and easier to work with subsets.</p> <p>For example you can use a Virtual library to only show you books with the Tag <i>"Unread"</i> or only books by <i>"My favorite author"</i> or only books in a particular series.</p> <p>More information and examples are available in the <a href="%s">User Manual</a>.</p> ''') % localize_user_manual_link( 'https://manual.calibre-ebook.com/virtual_libraries.html')) hl.setWordWrap(True) hl.setOpenExternalLinks(True) hl.setFrameStyle(hl.StyledPanel) gl.addWidget(hl, 0, 3, 4, 1) bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) gl.addWidget(bb, 4, 0, 1, 0) if editing: db = self.gui.current_db virt_libs = db.prefs.get('virtual_libraries', {}) for dex, vl in enumerate(sorted(virt_libs.keys(), key=sort_key)): self.vl_name.addItem(vl, virt_libs.get(vl, '')) if vl == editing: self.vl_name.setCurrentIndex(dex) self.original_index = dex self.original_search = virt_libs.get(editing, '') self.vl_text.setText(self.original_search) self.new_name = editing self.vl_name.currentIndexChanged[int].connect( self.name_index_changed) self.vl_name.lineEdit().textEdited.connect(self.name_text_edited) self.resize(self.sizeHint() + QSize(150, 25))
def __init__(self, parent=None): super(WebTab, self).__init__(parent) self.current = {'title': "[EMPTY]", 'address': ""} # address bar self.address_bar = AddressBar(parent=self) # webkit (the actual "web engine") self.webkit = WebView(parent=self) # set_prefix: app defined, carries str self.webkit.set_prefix.connect(self.address_bar.set_model) # CFG02 # javascript_state: app defined, carries bool self.webkit.javascript_state.connect(self.address_bar.set_bgcolor) # small label displaying instance ID and pending tab operations info_label = QLabel(parent=self) info_label.setText('?') # CFG02 # webkit_info: app defined, carries str self.webkit.attr.webkit_info.connect(info_label.setText) def update_address(qurl): """ The 'connect' gives a QUrl and setText receives a string; can't just connect setText Required because a 3XX HTTP redirection will change the address, and without updating, the address bar will be left stale AB02 AB03 """ self.current['address'] = qurl.toString() self.address_bar.setText(self.current['address']) # urlChanged carries QUrl; loadStarted carries nothing; # loadFinished carries bool; titleChanged carries str; # loadProgress carries int self.webkit.urlChanged.connect(update_address) self.webkit.loadStarted.connect(self.load_started) self.webkit.loadFinished.connect(self.load_finished) self.webkit.titleChanged.connect(self.save_title) self.webkit.loadProgress.connect(self.load_progress) def fill_notifier(message, request): """ sends a message to be displayed by the notifier """ notify(message + " " + request.url().toString()) # downloadRequested carries QNetworkRequest self.webkit.page().downloadRequested.connect( partial(fill_notifier, "download")) # unsupportedContent carries QNetworkReply self.webkit.page().unsupportedContent.connect( partial(fill_notifier, "unsupported")) # input area for access-key navigation self.nav_bar = NavigateInput(parent=self) # editingFinished carries nothing self.nav_bar.editingFinished.connect(self.webkit.clear_labels) # textEdited carries str self.nav_bar.textEdited.connect(self.webkit.akeynav) # nonvalid_tag (app defined) carries nothing self.webkit.nonvalid_tag.connect(self.nav_bar.clear) # 'corner' message and notification label, not on timer, smaller self.message_label = MessageLabel(self.webkit) def handle_hovered(link, title, content): """ When hovered, if ALT is pressed, show message label; hide otherwise """ if ((QApplication.keyboardModifiers() & Qt.AltModifier) and (link or title or content)): # ugly hack to ensure proper resizing; find a better way? self.message_label.hide() self.message_label.setText(link + " " + title + " " + content) self.message_label.show() else: self.message_label.hide() # linkHovered carries str, str, str self.webkit.page().linkHovered.connect(handle_hovered) def handle_signaled(title): """ We received a string through a signal; display it on the message label """ # if title: self.message_label.hide() self.message_label.setText(title) self.message_label.show() # show_message (app defined) carries str self.webkit.show_message.connect(handle_signaled) # loadStarted carries nothing self.webkit.loadStarted.connect(self.message_label.hide) # At the time navigation is requested load_requested is sent, and the # requested url is set as text in grey at the address bar. Once the # urlChanged signal is received, the actual url is set in black. # load_requested (app defined) carries str self.webkit.load_requested.connect( partial(self.address_bar.set_txt_color, color=QColor(128, 128, 128))) def hide_message_label(*_): """ WARNING scrollRequested carries int, int, QRect; star swallows all """ self.message_label.hide() self.webkit.page().scrollRequested.connect(hide_message_label) # focus_webkit (app defined) carries nothing self.webkit.hide_overlay.connect(self.message_label.hide) self.webkit.focus_webkit.connect(self.address_bar.restore) # progress bar self.pbar = QProgressBar(self) self.pbar.setRange(0, 100) self.pbar.setTextVisible(False) self.pbar.setVisible(False) self.pbar.setMaximumHeight(7) # search in page self.search_frame = SearchFrame(parent=self) # NAV20 # textChanged carries str self.search_frame.search_line.textChanged.connect(self.do_search) # layout grid = QGridLayout(self) grid.setSpacing(0) grid.setVerticalSpacing(0) grid.setContentsMargins(0, 0, 0, 0) grid.setRowStretch(1, 1) grid.addWidget(info_label, 0, 0, 1, 1) grid.addWidget(self.address_bar, 0, 1, 1, 1) grid.addWidget(self.nav_bar, 0, 2, 1, 1) grid.addWidget(self.webkit, 1, 0, 1, 3) grid.addWidget(self.search_frame, 2, 0, 1, 3) grid.addWidget(self.pbar, 3, 0, 1, 3) def show_search(): """ One-time callback for QShortcut NAV20 """ self.search_frame.setVisible(True) self.search_frame.search_line.setFocus() def hide_search(): """ One-time callback for QShortcut NAV20 """ self.search_frame.setVisible(False) self.webkit.findText("") self.webkit.setFocus() def navigate_completion(key=Qt.Key_Down): """ Sends an "arrow press" to the completion popup to navigate results. Not the best way to do this. It would be better to find out what function is being called by that arrow press. AB01 """ event = QKeyEvent(QEvent.KeyPress, key, Qt.NoModifier) self.address_bar.completer().popup().keyPressEvent(event) # the star swallows all arguments that aren't named 'store' def reset_addressbar(*, store=False): """ Restore the address bar to its original address and color (it could have changed because of a hover event). Optionally, store the original address in the clipboard. AB03 """ if self.current['address']: self.address_bar.set_txt_color(self.current['address'], color=QColor(0, 0, 0)) if store: clipboard(self.current['address']) # urlChanged carries QUrl (ignored) self.webkit.urlChanged.connect(reset_addressbar) def enter_address_bar(clear=True): """ do not try entering the address bar if a load is in progress; do an 'stop' first AB00 """ if 'in_page_load' not in self.webkit.attr: if clear: self.address_bar.clear_and_focus() else: self.address_bar.setFocus() def cancel(): """ if we're in the middle of loading the document, stop loading. Otherwise, hide the message label. The general concept is to reach a basic state. """ if 'in_page_load' not in self.webkit.attr: self.message_label.hide() self.webkit.update() else: self.webkit.stop() set_shortcuts([ # search NAV20 ("G", self.webkit, show_search), ("Escape", self.search_frame, hide_search), ("Return", self.search_frame, self.do_search), ("Ctrl+J", self.search_frame, self.do_search), # go to page AB00 ("Ctrl+J", self.address_bar, partial(self.webkit.navigate, self.address_bar)), ("Return", self.address_bar, partial(self.webkit.navigate, self.address_bar)), # address bar interaction ("A", self.webkit, cancel), ("Ctrl+L", self.webkit, enter_address_bar), # AB00 ("Ctrl+Shift+L", self.webkit, partial(enter_address_bar, clear=False)), ("Escape", self.address_bar, self.webkit.setFocus), # AB00 ("Ctrl+I", self.address_bar, navigate_completion), # AB01 ("Ctrl+P", self.address_bar, partial(navigate_completion, Qt.Key_Up)), # in-page element navigation ("Ñ", self, self.enter_nav), # NAV11 (";", self, self.enter_nav), # DOM01 ("Ctrl+Ñ", self, partial(self.enter_nav, target="titles")), # toggle ("Q", self.webkit, self.toggle_script), # TOG01 # clipboard ("E", self, partial(reset_addressbar, store=True)) # CB05 ])
class LLT_ConjAdd(QMainWindow): def __init__(self): super(LLT_ConjAdd, self).__init__() self.w = QWidget() self.setCentralWidget(self.w) #construct GUI self.setWindowTitle("Verb Conjugations") self.setGeometry(0, 0, 900, 600) self.conjDicList = [] self.wordList = [] self.newDic = {'INF': '', 'IND': '', 'SUB': '', 'IMP': ''} self.headingLab = QLabel("Add New Conjugation Table") self.wordInfLab = QLabel("Infinitive") self.wordInfLab.setAlignment(Qt.AlignRight) self.infEntry = QLineEdit() #INDICATIVE FORM TABLE self.indLab = QLabel("Indicative") self.indYoLab = QLabel("Yo") self.indYoLab.setAlignment(Qt.AlignRight) self.indTuLab = QLabel("Tú") self.indTuLab.setAlignment(Qt.AlignRight) self.indUstLab = QLabel("Él/la, Ud") self.indUstLab.setAlignment(Qt.AlignRight) self.indNosLab = QLabel("Nosotros") self.indNosLab.setAlignment(Qt.AlignRight) self.indUstdsLab = QLabel("Ellos/as, Uds") self.indUstdsLab.setAlignment(Qt.AlignRight) self.indPresLab = QLabel("Present") self.indPretLab = QLabel("Preterite") self.indImpLab = QLabel("Imperfect") self.indFutLab = QLabel("Future") self.indCondLab = QLabel("Conditional") self.entryGridInd = QGridLayout() for i in range(5): for j in range(5): self.entry = QLineEdit() self.entryGridInd.addWidget(self.entry, i, j) #SUBJUNCTIVE FORM TABLE self.subLab = QLabel("Subjunctive") self.subYoLab = QLabel("Yo") self.subYoLab.setAlignment(Qt.AlignRight) self.subTuLab = QLabel("Tú") self.subTuLab.setAlignment(Qt.AlignRight) self.subUstLab = QLabel("Él/la, Ud") self.subUstLab.setAlignment(Qt.AlignRight) self.subNosLab = QLabel("Nosotros/as") self.subNosLab.setAlignment(Qt.AlignRight) self.subUstdsLab = QLabel("Ellos/as, Uds") self.subUstdsLab.setAlignment(Qt.AlignRight) self.subPresLab = QLabel("Present") self.subImpLab = QLabel("Imperfect") self.subFutLab = QLabel("Future") self.entryGridSub = QGridLayout() for i in range(5): for j in range(3): self.entry = QLineEdit() self.entryGridSub.addWidget(self.entry, i, j) #IMPERATIVE FORM TABLE self.impvLab = QLabel("Imperative") self.impvTu = QLabel("Tú") self.impvTu.setAlignment(Qt.AlignRight) self.impvUd = QLabel("Usted") self.impvUd.setAlignment(Qt.AlignRight) self.impvNos = QLabel("Nosotros/as") self.impvNos.setAlignment(Qt.AlignRight) self.impvUdes = QLabel("Ustedes") self.impvUdes.setAlignment(Qt.AlignRight) self.impvAffLab = QLabel("Affirmative") self.impvNegLab = QLabel("Negative") self.entryGridImpv = QGridLayout() for i in range(4): for j in range(2): self.entry = QLineEdit() self.entryGridImpv.addWidget(self.entry, i, j) self.checkBut = QPushButton("Check") self.checkBut.clicked.connect(self.check) self.saveBut = QPushButton("Save") self.saveBut.clicked.connect(self.save) self.newBut = QPushButton("New Word") self.newBut.clicked.connect(self.new) self.clearBut = QPushButton("Clear") self.clearBut.clicked.connect(self.clear) self.quitBut = QPushButton("Quit") self.quitBut.clicked.connect(self.quit) self.theGrid = QGridLayout() self.theGrid.addWidget(self.headingLab, 0, 0) self.theGrid.addWidget(self.wordInfLab, 1, 0) self.theGrid.addWidget(self.infEntry, 1, 1) self.theGrid.addWidget(self.indLab, 3, 0) self.theGrid.addWidget(self.indPresLab, 3, 1) self.theGrid.addWidget(self.indPretLab, 3, 2) self.theGrid.addWidget(self.indImpLab, 3, 3) self.theGrid.addWidget(self.indFutLab, 3, 4) self.theGrid.addWidget(self.indCondLab, 3, 5) self.theGrid.addWidget(self.indYoLab, 4, 0) self.theGrid.addWidget(self.indTuLab, 5, 0) self.theGrid.addWidget(self.indUstLab, 6, 0) self.theGrid.addWidget(self.indNosLab, 7, 0) self.theGrid.addWidget(self.indUstdsLab, 8, 0) self.theGrid.addLayout(self.entryGridInd, 4, 1, 5, 5) self.theGrid.addWidget(self.subLab, 11, 0) self.theGrid.addWidget(self.subPresLab, 11, 1) self.theGrid.addWidget(self.subImpLab, 11, 2) self.theGrid.addWidget(self.subFutLab, 11, 3) self.theGrid.addWidget(self.subYoLab, 12, 0) self.theGrid.addWidget(self.subTuLab, 13, 0) self.theGrid.addWidget(self.subUstLab, 14, 0) self.theGrid.addWidget(self.subNosLab, 15, 0) self.theGrid.addWidget(self.subUstdsLab, 16, 0) self.theGrid.addLayout(self.entryGridSub, 12, 1, 5, 3) self.theGrid.addWidget(self.impvLab, 19, 0) self.theGrid.addWidget(self.impvAffLab, 19, 1) self.theGrid.addWidget(self.impvNegLab, 19, 2) self.theGrid.addWidget(self.impvTu, 20, 0) self.theGrid.addWidget(self.impvUd, 21, 0) self.theGrid.addWidget(self.impvNos, 22, 0) self.theGrid.addWidget(self.impvUdes, 23, 0) self.theGrid.addLayout(self.entryGridImpv, 20, 1, 4, 2) self.theGrid.addWidget(self.checkBut, 19, 5) self.theGrid.addWidget(self.saveBut, 20, 5) self.theGrid.addWidget(self.clearBut, 21, 5) self.theGrid.addWidget(self.newBut, 22, 5) self.theGrid.addWidget(self.quitBut, 23, 5) for i in range(24): self.theGrid.setRowStretch(i, 1) for j in range(6): self.theGrid.setColumnStretch(j, 1) self.w.setLayout(self.theGrid) self.getDic() def check(self): word = self.infEntry.text().upper() if word in self.wordList: msgBox = QMessageBox() msgBox.setText(word + ' already in dictionary') msgBox.exec_() else: msgBox = QMessageBox() msgBox.setText(word + ' not in dictionary yet') msgBox.exec_() def save(self): infinitive = self.infEntry.text().upper() if infinitive in self.wordList: msgBox = QMessageBox() msgBox.setText(infinitive + ' already in dictionary') msgBox.exec_() else: self.wordList.append(infinitive) indList = [] subList = [] impList = [] for i in range(self.entryGridInd.count()): item = self.entryGridInd.itemAt(i) child = item.widget() indList.append(child.text().upper()) for i in range(self.entryGridSub.count()): item = self.entryGridSub.itemAt(i) child = item.widget() subList.append(child.text().upper()) for i in range(self.entryGridImpv.count()): item = self.entryGridImpv.itemAt(i) child = item.widget() impList.append(child.text().upper()) self.newDic['INF'] = infinitive self.newDic['IND'] = indList self.newDic['SUB'] = subList self.newDic['IMP'] = impList self.conjDicList.append(self.newDic) c = open('conj.txt', 'w') json.dump(self.conjDicList, c) c.close() msgBox = QMessageBox() msgBox.setText(infinitive + ' has been saved') msgBox.exec_() self.newDic = {'INF': '', 'IND': '', 'SUB': '', 'IMP': ''} def new(self): confirm = QMessageBox.question( self.w, 'New Word', 'Are you sure you want to clear all entries \nand start a new word?', QMessageBox.Yes | QMessageBox.No) if confirm == QMessageBox.Yes: self.infEntry.clear() for i in range(self.entryGridInd.count()): item = self.entryGridInd.itemAt(i) child = item.widget() child.clear() for i in range(self.entryGridSub.count()): item = self.entryGridSub.itemAt(i) child = item.widget() child.clear() for i in range(self.entryGridImpv.count()): item = self.entryGridImpv.itemAt(i) child = item.widget() child.clear() else: pass def clear(self): confirm = QMessageBox.question( self.w, 'Clear', 'Are you sure you want to clear all entries?', QMessageBox.Yes | QMessageBox.No) if confirm == QMessageBox.Yes: self.infEntry.clear() for i in range(self.entryGridInd.count()): item = self.entryGridInd.itemAt(i) child = item.widget() child.clear() for i in range(self.entryGridSub.count()): item = self.entryGridSub.itemAt(i) child = item.widget() child.clear() for i in range(self.entryGridImpv.count()): item = self.entryGridImpv.itemAt(i) child = item.widget() child.clear() else: pass def quit(self): confirm = QMessageBox.question(self.w, 'Quit', 'Are you sure you want to exit?', QMessageBox.Yes | QMessageBox.No) if confirm == QMessageBox.Yes: self.close() else: pass def getDic(self): try: c = open('conj.txt', 'r') self.conjDicList = json.load(c) c.close() for item in self.conjDicList: self.wordList.append(item['INF']) except: self.conjDicList = []
class ExtraCustomization(DeviceConfigTab): # {{{ def __init__(self, extra_customization_message, extra_customization_choices, device_settings): super(ExtraCustomization, self).__init__() debug_print("ExtraCustomization.__init__ - extra_customization_message=", extra_customization_message) debug_print("ExtraCustomization.__init__ - extra_customization_choices=", extra_customization_choices) debug_print("ExtraCustomization.__init__ - device_settings.extra_customization=", device_settings.extra_customization) debug_print("ExtraCustomization.__init__ - device_settings=", device_settings) self.extra_customization_message = extra_customization_message self.l = QVBoxLayout(self) self.setLayout(self.l) options_group = QGroupBox(_("Extra driver customization options"), self) self.l.addWidget(options_group) self.extra_layout = QGridLayout() self.extra_layout.setObjectName("extra_layout") options_group.setLayout(self.extra_layout) if extra_customization_message: extra_customization_choices = extra_customization_choices or {} def parse_msg(m): msg, _, tt = m.partition(':::') if m else ('', '', '') return msg.strip(), textwrap.fill(tt.strip(), 100) if isinstance(extra_customization_message, list): self.opt_extra_customization = [] if len(extra_customization_message) > 6: row_func = lambda x, y: ((x/2) * 2) + y col_func = lambda x: x%2 else: row_func = lambda x, y: x*2 + y col_func = lambda x: 0 for i, m in enumerate(extra_customization_message): label_text, tt = parse_msg(m) if not label_text: self.opt_extra_customization.append(None) continue if isinstance(device_settings.extra_customization[i], bool): self.opt_extra_customization.append(QCheckBox(label_text)) self.opt_extra_customization[-1].setToolTip(tt) self.opt_extra_customization[i].setChecked(bool(device_settings.extra_customization[i])) elif i in extra_customization_choices: cb = QComboBox(self) self.opt_extra_customization.append(cb) l = QLabel(label_text) l.setToolTip(tt), cb.setToolTip(tt), l.setBuddy(cb), cb.setToolTip(tt) for li in sorted(extra_customization_choices[i]): self.opt_extra_customization[i].addItem(li) cb.setCurrentIndex(max(0, cb.findText(device_settings.extra_customization[i]))) else: self.opt_extra_customization.append(QLineEdit(self)) l = QLabel(label_text) l.setToolTip(tt) self.opt_extra_customization[i].setToolTip(tt) l.setBuddy(self.opt_extra_customization[i]) l.setWordWrap(True) self.opt_extra_customization[i].setText(device_settings.extra_customization[i]) self.opt_extra_customization[i].setCursorPosition(0) self.extra_layout.addWidget(l, row_func(i + 2, 0), col_func(i)) self.extra_layout.addWidget(self.opt_extra_customization[i], row_func(i + 2, 1), col_func(i)) spacerItem1 = QSpacerItem(10, 10, QSizePolicy.Minimum, QSizePolicy.Expanding) self.extra_layout.addItem(spacerItem1, row_func(i + 2 + 2, 1), 0, 1, 2) self.extra_layout.setRowStretch(row_func(i + 2 + 2, 1), 2) else: self.opt_extra_customization = QLineEdit() label_text, tt = parse_msg(extra_customization_message) l = QLabel(label_text) l.setToolTip(tt) l.setBuddy(self.opt_extra_customization) l.setWordWrap(True) if device_settings.extra_customization: self.opt_extra_customization.setText(device_settings.extra_customization) self.opt_extra_customization.setCursorPosition(0) self.opt_extra_customization.setCursorPosition(0) self.extra_layout.addWidget(l, 0, 0) self.extra_layout.addWidget(self.opt_extra_customization, 1, 0) def extra_customization(self): ec = [] if self.extra_customization_message: if isinstance(self.extra_customization_message, list): for i in range(0, len(self.extra_customization_message)): if self.opt_extra_customization[i] is None: ec.append(None) continue if hasattr(self.opt_extra_customization[i], 'isChecked'): ec.append(self.opt_extra_customization[i].isChecked()) elif hasattr(self.opt_extra_customization[i], 'currentText'): ec.append(unicode_type(self.opt_extra_customization[i].currentText()).strip()) else: ec.append(unicode_type(self.opt_extra_customization[i].text()).strip()) else: ec = unicode_type(self.opt_extra_customization.text()).strip() if not ec: ec = None return ec @property def has_extra_customizations(self): debug_print("ExtraCustomization::has_extra_customizations - self.extra_customization_message", self.extra_customization_message) return self.extra_customization_message and len(self.extra_customization_message) > 0
def __init__(self, parent=None): super(WebTab, self).__init__(parent) self.current = { 'title': "[EMPTY]", 'address': "" } # address bar self.address_bar = AddressBar(parent=self) # webkit (the actual "web engine") self.webkit = WebView(parent=self) # set_prefix: app defined, carries str self.webkit.set_prefix.connect(self.address_bar.set_model) # CFG02 # javascript_state: app defined, carries bool self.webkit.javascript_state.connect(self.address_bar.set_bgcolor) # small label displaying instance ID and pending tab operations info_label = QLabel(parent=self) info_label.setText('?') # CFG02 # webkit_info: app defined, carries str self.webkit.attr.webkit_info.connect(info_label.setText) def update_address(qurl): """ The 'connect' gives a QUrl and setText receives a string; can't just connect setText Required because a 3XX HTTP redirection will change the address, and without updating, the address bar will be left stale AB02 AB03 """ self.current['address'] = qurl.toString() self.address_bar.setText(self.current['address']) # urlChanged carries QUrl; loadStarted carries nothing; # loadFinished carries bool; titleChanged carries str; # loadProgress carries int self.webkit.urlChanged.connect(update_address) self.webkit.loadStarted.connect(self.load_started) self.webkit.loadFinished.connect(self.load_finished) self.webkit.titleChanged.connect(self.save_title) self.webkit.loadProgress.connect(self.load_progress) def fill_notifier(message, request): """ sends a message to be displayed by the notifier """ notify(message + " " + request.url().toString()) # downloadRequested carries QNetworkRequest self.webkit.page().downloadRequested.connect( partial(fill_notifier, "download")) # unsupportedContent carries QNetworkReply self.webkit.page().unsupportedContent.connect( partial(fill_notifier, "unsupported")) # input area for access-key navigation self.nav_bar = NavigateInput(parent=self) # editingFinished carries nothing self.nav_bar.editingFinished.connect(self.webkit.clear_labels) # textEdited carries str self.nav_bar.textEdited.connect(self.webkit.akeynav) # nonvalid_tag (app defined) carries nothing self.webkit.nonvalid_tag.connect(self.nav_bar.clear) # 'corner' message and notification label, not on timer, smaller self.message_label = MessageLabel(self.webkit) def handle_hovered(link, title, content): """ When hovered, if ALT is pressed, show message label; hide otherwise """ if ((QApplication.keyboardModifiers() & Qt.AltModifier) and (link or title or content)): # ugly hack to ensure proper resizing; find a better way? self.message_label.hide() self.message_label.setText( link + " " + title + " " + content) self.message_label.show() else: self.message_label.hide() # linkHovered carries str, str, str self.webkit.page().linkHovered.connect(handle_hovered) def handle_signaled(title): """ We received a string through a signal; display it on the message label """ # if title: self.message_label.hide() self.message_label.setText(title) self.message_label.show() # show_message (app defined) carries str self.webkit.show_message.connect(handle_signaled) # loadStarted carries nothing self.webkit.loadStarted.connect(self.message_label.hide) # At the time navigation is requested load_requested is sent, and the # requested url is set as text in grey at the address bar. Once the # urlChanged signal is received, the actual url is set in black. # load_requested (app defined) carries str self.webkit.load_requested.connect( partial(self.address_bar.set_txt_color, color=QColor(128, 128, 128))) def hide_message_label(*_): """ WARNING scrollRequested carries int, int, QRect; star swallows all """ self.message_label.hide() self.webkit.page().scrollRequested.connect(hide_message_label) # focus_webkit (app defined) carries nothing self.webkit.hide_overlay.connect(self.message_label.hide) self.webkit.focus_webkit.connect(self.address_bar.restore) # progress bar self.pbar = QProgressBar(self) self.pbar.setRange(0, 100) self.pbar.setTextVisible(False) self.pbar.setVisible(False) self.pbar.setMaximumHeight(7) # search in page self.search_frame = SearchFrame(parent=self) # NAV20 # textChanged carries str self.search_frame.search_line.textChanged.connect(self.do_search) # layout grid = QGridLayout(self) grid.setSpacing(0) grid.setVerticalSpacing(0) grid.setContentsMargins(0, 0, 0, 0) grid.setRowStretch(1, 1) grid.addWidget(info_label, 0, 0, 1, 1) grid.addWidget(self.address_bar, 0, 1, 1, 1) grid.addWidget(self.nav_bar, 0, 2, 1, 1) grid.addWidget(self.webkit, 1, 0, 1, 3) grid.addWidget(self.search_frame, 2, 0, 1, 3) grid.addWidget(self.pbar, 3, 0, 1, 3) def show_search(): """ One-time callback for QShortcut NAV20 """ self.search_frame.setVisible(True) self.search_frame.search_line.setFocus() def hide_search(): """ One-time callback for QShortcut NAV20 """ self.search_frame.setVisible(False) self.webkit.findText("") self.webkit.setFocus() def navigate_completion(key=Qt.Key_Down): """ Sends an "arrow press" to the completion popup to navigate results. Not the best way to do this. It would be better to find out what function is being called by that arrow press. AB01 """ event = QKeyEvent(QEvent.KeyPress, key, Qt.NoModifier) self.address_bar.completer().popup().keyPressEvent(event) # the star swallows all arguments that aren't named 'store' def reset_addressbar(*, store=False): """ Restore the address bar to its original address and color (it could have changed because of a hover event). Optionally, store the original address in the clipboard. AB03 """ if self.current['address']: self.address_bar.set_txt_color(self.current['address'], color=QColor(0, 0, 0)) if store: clipboard(self.current['address']) # urlChanged carries QUrl (ignored) self.webkit.urlChanged.connect(reset_addressbar) def enter_address_bar(clear=True): """ do not try entering the address bar if a load is in progress; do an 'stop' first AB00 """ if 'in_page_load' not in self.webkit.attr: if clear: self.address_bar.clear_and_focus() else: self.address_bar.setFocus() def cancel(): """ if we're in the middle of loading the document, stop loading. Otherwise, hide the message label. The general concept is to reach a basic state. """ if 'in_page_load' not in self.webkit.attr: self.message_label.hide() self.webkit.update() else: self.webkit.stop() set_shortcuts([ # search NAV20 ("G", self.webkit, show_search), ("Escape", self.search_frame, hide_search), ("Return", self.search_frame, self.do_search), ("Ctrl+J", self.search_frame, self.do_search), # go to page AB00 ("Ctrl+J", self.address_bar, partial( self.webkit.navigate, self.address_bar)), ("Return", self.address_bar, partial( self.webkit.navigate, self.address_bar)), # address bar interaction ("A", self.webkit, cancel), ("Ctrl+L", self.webkit, enter_address_bar), # AB00 ("Ctrl+Shift+L", self.webkit, partial( enter_address_bar, clear=False)), ("Escape", self.address_bar, self.webkit.setFocus), # AB00 ("Ctrl+I", self.address_bar, navigate_completion), # AB01 ("Ctrl+P", self.address_bar, partial( navigate_completion, Qt.Key_Up)), # in-page element navigation ("Ñ", self, self.enter_nav), # NAV11 (";", self, self.enter_nav), # DOM01 ("Ctrl+Ñ", self, partial(self.enter_nav, target="titles")), # toggle ("Q", self.webkit, self.toggle_script), # TOG01 # clipboard ("E", self, partial(reset_addressbar, store=True)) # CB05 ])
class ExtendedGroupBox(DeviceOptionsGroupBox): """The options group for KoboTouchExtended.""" def __init__(self, parent, device): """Set up driver config options group.""" super(ExtendedGroupBox, self).__init__( parent, device, _("Extended driver") # noqa: F821 ) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.extra_features_checkbox = create_checkbox( _("Enable Extended Kobo Features"), # noqa: F821 _("Choose whether to enable extra customizations"), # noqa: F821 device.get_pref("extra_features"), ) self.upload_encumbered_checkbox = create_checkbox( _("Upload DRM-encumbered ePub files"), # noqa: F821 _( # noqa: F821 "Select this to upload ePub files encumbered by DRM. If this " "is not selected, it is a fatal error to upload an encumbered " "file"), device.get_pref("upload_encumbered"), ) self.skip_failed_checkbox = create_checkbox( _("Silently Ignore Failed Conversions"), # noqa: F821 _( # noqa: F821 "Select this to not upload any book that fails conversion to " "kepub. If this is not selected, the upload process will be " "stopped at the first book that fails. If this is selected, " "failed books will be silently removed from the upload queue." ), device.get_pref("skip_failed"), ) self.hyphenate_checkbox = create_checkbox( _("Hyphenate Files"), # noqa: F821 _( # noqa: F821 "Select this to add a CSS file which enables hyphenation. The " "language used will be the language defined for the book in " "calibre. Please see the README file for directions on " "updating hyphenation dictionaries."), device.get_pref("hyphenate"), ) self.smarten_punctuation_checkbox = create_checkbox( _("Smarten Punctuation"), # noqa: F821 _("Select this to smarten punctuation in the ePub"), # noqa: F821 device.get_pref("smarten_punctuation"), ) self.clean_markup_checkbox = create_checkbox( _("Clean up ePub Markup"), # noqa: F821 _("Select this to clean up the internal ePub markup." ), # noqa: F821 device.get_pref("clean_markup"), ) self.file_copy_dir_checkbox = create_checkbox( _("Copy generated KePub files to a directory"), # noqa: F821 _( # noqa: F821 "Enter an absolute directory path to copy all generated KePub " "files into for debugging purposes."), device.get_pref("file_copy_dir"), ) self.file_copy_dir_label = QLabel( _("Copy generated KePub files to a directory") # noqa: F821 ) self.file_copy_dir_edit = QLineEdit(self) self.file_copy_dir_edit.setToolTip( _( # noqa: F821 "Enter an absolute directory path to copy all generated KePub " "files into for debugging purposes.")) self.file_copy_dir_edit.setText(device.get_pref("file_copy_dir")) self.file_copy_dir_label.setBuddy(self.file_copy_dir_edit) self.full_page_numbers_checkbox = create_checkbox( _("Use full book page numbers"), # noqa: F821 _( # noqa: F821 "Select this to show page numbers for the whole book, instead " "of each chapter. This will also affect regular ePub page " "number display!"), device.get_pref("full_page_numbers"), ) self.disable_hyphenation_checkbox = create_checkbox( _("Disable hyphenation"), # noqa: F821 _("Select this to disable hyphenation for books."), # noqa: F821 device.get_pref("disable_hyphenation"), ) self.opt_kepub_hyphenate_chars_label = QLabel( _("Minimum word length to hyphenate") + ":" # noqa: F821 ) self.opt_kepub_hyphenate_chars = QSpinBox(self) self.opt_kepub_hyphenate_chars_label.setBuddy( self.opt_kepub_hyphenate_chars) self.opt_kepub_hyphenate_chars.setObjectName( "opt_kepub_hyphenate_chars") self.opt_kepub_hyphenate_chars.setSpecialValueText( _("Disabled")) # noqa: F821 self.opt_kepub_hyphenate_chars.valueChanged.connect( functools.partial( common.intValueChanged, self.opt_kepub_hyphenate_chars, _("character"), # noqa: F821 _("characters"), # noqa: F821 )) self.opt_kepub_hyphenate_chars.setValue( device.get_pref("hyphenate_chars")) self.opt_kepub_hyphenate_chars_before_label = QLabel( _("Minimum characters before hyphens") + ":" # noqa: F821 ) self.opt_kepub_hyphenate_chars_before = QSpinBox(self) self.opt_kepub_hyphenate_chars_before_label.setBuddy( self.opt_kepub_hyphenate_chars_before) self.opt_kepub_hyphenate_chars_before.setObjectName( "opt_kepub_hyphenate_chars_before") self.opt_kepub_hyphenate_chars_before.valueChanged.connect( functools.partial( common.intValueChanged, self.opt_kepub_hyphenate_chars_before, _("character"), # noqa: F821 _("characters"), # noqa: F821 )) self.opt_kepub_hyphenate_chars_before.setMinimum(2) self.opt_kepub_hyphenate_chars_before.setValue( device.get_pref("hyphenate_chars_before")) self.opt_kepub_hyphenate_chars_after_label = QLabel( _("Minimum characters after hyphens") + ":" # noqa: F821 ) self.opt_kepub_hyphenate_chars_after = QSpinBox(self) self.opt_kepub_hyphenate_chars_after_label.setBuddy( self.opt_kepub_hyphenate_chars_after) self.opt_kepub_hyphenate_chars_after.setObjectName( "opt_kepub_hyphenate_chars_after") self.opt_kepub_hyphenate_chars_after.valueChanged.connect( functools.partial( common.intValueChanged, self.opt_kepub_hyphenate_chars_after, _("character"), # noqa: F821 _("characters"), # noqa: F821 )) self.opt_kepub_hyphenate_chars_after.setMinimum(2) self.opt_kepub_hyphenate_chars_after.setValue( device.get_pref("hyphenate_chars_after")) self.opt_kepub_hyphenate_limit_lines_label = QLabel( _("Maximum consecutive hyphenated lines") + ":" # noqa: F821 ) self.opt_kepub_hyphenate_limit_lines = QSpinBox(self) self.opt_kepub_hyphenate_limit_lines_label.setBuddy( self.opt_kepub_hyphenate_limit_lines) self.opt_kepub_hyphenate_limit_lines.setObjectName( "opt_kepub_hyphenate_limit_lines") self.opt_kepub_hyphenate_limit_lines.setSpecialValueText( _("Disabled") # noqa: F821 ) self.opt_kepub_hyphenate_limit_lines.valueChanged.connect( functools.partial( common.intValueChanged, self.opt_kepub_hyphenate_limit_lines, _("line"), # noqa: F821 _("lines"), # noqa: F821 )) self.opt_kepub_hyphenate_limit_lines.setValue( device.get_pref("hyphenate_limit_lines")) self.options_layout.addWidget(self.extra_features_checkbox, 0, 0, 1, 1) self.options_layout.addWidget(self.upload_encumbered_checkbox, 0, 1, 1, 1) self.options_layout.addWidget(self.skip_failed_checkbox, 1, 0, 1, 1) self.options_layout.addWidget(self.hyphenate_checkbox, 1, 1, 1, 1) self.options_layout.addWidget(self.smarten_punctuation_checkbox, 2, 1, 1, 1) self.options_layout.addWidget(self.clean_markup_checkbox, 3, 0, 1, 1) self.options_layout.addWidget(self.file_copy_dir_label, 4, 0, 1, 1) self.options_layout.addWidget(self.file_copy_dir_edit, 4, 1, 1, 1) self.options_layout.addWidget(self.full_page_numbers_checkbox, 5, 0, 1, 1) self.options_layout.addWidget(self.disable_hyphenation_checkbox, 5, 1, 1, 1) self.options_layout.addWidget(self.opt_kepub_hyphenate_chars_label, 6, 0, 1, 1) self.options_layout.addWidget(self.opt_kepub_hyphenate_chars, 6, 1, 1, 1) self.options_layout.addWidget( self.opt_kepub_hyphenate_chars_before_label, 7, 0, 1, 1) self.options_layout.addWidget(self.opt_kepub_hyphenate_chars_before, 7, 1, 1, 1) self.options_layout.addWidget( self.opt_kepub_hyphenate_chars_after_label, 8, 0, 1, 1) self.options_layout.addWidget(self.opt_kepub_hyphenate_chars_after, 8, 1, 1, 1) self.options_layout.addWidget( self.opt_kepub_hyphenate_limit_lines_label, 9, 0, 1, 1) self.options_layout.addWidget(self.opt_kepub_hyphenate_limit_lines, 9, 1, 1, 1) self.options_layout.setRowStretch(10, 2) @property def extra_features(self): """Determine if Kobo extra features are enabled.""" return self.extra_features_checkbox.isChecked() @property def upload_encumbered(self): """Determine if DRM-encumbered files will be uploaded.""" return self.upload_encumbered_checkbox.isChecked() @property def skip_failed(self): """Determine if failed conversions will be skipped.""" return self.skip_failed_checkbox.isChecked() @property def hyphenate(self): """Determine if hyphenation should be enabled.""" return self.hyphenate_checkbox.isChecked() @property def smarten_punctuation(self): """Determine if punctuation should be converted to smart punctuation.""" return self.smarten_punctuation_checkbox.isChecked() @property def clean_markup(self): """Determine if additional markup cleanup will be done.""" return self.clean_markup_checkbox.isChecked() @property def full_page_numbers(self): """Determine if full-book page numbers will be displayed.""" return self.full_page_numbers_checkbox.isChecked() @property def disable_hyphenation(self): """Determine if hyphenation should be disabled.""" return self.disable_hyphenation_checkbox.isChecked() @property def file_copy_dir(self): """Determine where to copy converted KePub books to.""" return self.file_copy_dir_edit.text().strip() @property def hyphenate_chars(self): return self.opt_kepub_hyphenate_chars.value() @property def hyphenate_chars_before(self): return self.opt_kepub_hyphenate_chars_before.value() @property def hyphenate_chars_after(self): return self.opt_kepub_hyphenate_chars_after.value() @property def hyphenate_limit_lines(self): return self.opt_kepub_hyphenate_limit_lines.value()
class CollectionsGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(CollectionsGroupBox, self).__init__(parent, device) self.setTitle(_("Collections")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.setCheckable(True) self.setChecked(device.get_pref('manage_collections')) self.setToolTip(wrap_msg(_('Create new bookshelves on the Kobo if they do not exist. This is only for firmware V2.0.0 or later.'))) self.collections_columns_label = QLabel(_('Collections Columns:')) self.collections_columns_edit = QLineEdit(self) self.collections_columns_edit.setToolTip(_('The Kobo from firmware V2.0.0 supports bookshelves.' ' These are created on the Kobo. ' + 'Specify a tags type column for automatic management.')) self.collections_columns_edit.setText(device.get_pref('collections_columns')) self.create_collections_checkbox = create_checkbox( _("Create Collections"), _('Create new bookshelves on the Kobo if they do not exist. This is only for firmware V2.0.0 or later.'), device.get_pref('create_collections') ) self.delete_empty_collections_checkbox = create_checkbox( _('Delete Empty Bookshelves'), _('Delete any empty bookshelves from the Kobo when syncing is finished. This is only for firmware V2.0.0 or later.'), device.get_pref('delete_empty_collections') ) self.ignore_collections_names_label = QLabel(_('Ignore Collections:')) self.ignore_collections_names_edit = QLineEdit(self) self.ignore_collections_names_edit.setToolTip(_('List the names of collections to be ignored by ' + 'the collection management. The collections listed ' + 'will not be changed. Names are separated by commas.')) self.ignore_collections_names_edit.setText(device.get_pref('ignore_collections_names')) self.options_layout.addWidget(self.collections_columns_label, 1, 0, 1, 1) self.options_layout.addWidget(self.collections_columns_edit, 1, 1, 1, 1) self.options_layout.addWidget(self.create_collections_checkbox, 2, 0, 1, 2) self.options_layout.addWidget(self.delete_empty_collections_checkbox, 3, 0, 1, 2) self.options_layout.addWidget(self.ignore_collections_names_label, 4, 0, 1, 1) self.options_layout.addWidget(self.ignore_collections_names_edit, 4, 1, 1, 1) self.options_layout.setRowStretch(4, 1) @property def manage_collections(self): return self.isChecked() @property def collections_columns(self): return self.collections_columns_edit.text().strip() @property def create_collections(self): return self.create_collections_checkbox.isChecked() @property def delete_empty_collections(self): return self.delete_empty_collections_checkbox.isChecked() @property def ignore_collections_names(self): return self.ignore_collections_names_edit.text().strip()
def __init__(self, gui, existing_names, editing=None): QDialog.__init__(self, gui) self.gui = gui self.existing_names = existing_names if editing: self.setWindowTitle(_('Edit virtual library')) else: self.setWindowTitle(_('Create virtual library')) self.setWindowIcon(QIcon(I('lt.png'))) gl = QGridLayout() self.setLayout(gl) self.la1 = la1 = QLabel(_('Virtual library &name:')) gl.addWidget(la1, 0, 0) self.vl_name = QComboBox() self.vl_name.setEditable(True) self.vl_name.lineEdit().setMaxLength(MAX_VIRTUAL_LIBRARY_NAME_LENGTH) la1.setBuddy(self.vl_name) gl.addWidget(self.vl_name, 0, 1) self.editing = editing self.saved_searches_label = QLabel('') self.saved_searches_label.setTextInteractionFlags(Qt.TextSelectableByMouse) gl.addWidget(self.saved_searches_label, 2, 0, 1, 2) self.la2 = la2 = QLabel(_('&Search expression:')) gl.addWidget(la2, 1, 0) self.vl_text = QLineEdit() self.vl_text.textChanged.connect(self.search_text_changed) la2.setBuddy(self.vl_text) gl.addWidget(self.vl_text, 1, 1) self.vl_text.setText(_build_full_search_string(self.gui)) self.sl = sl = QLabel('<p>'+_('Create a virtual library based on: ')+ ('<a href="author.{0}">{0}</a>, ' '<a href="tag.{1}">{1}</a>, ' '<a href="publisher.{2}">{2}</a>, ' '<a href="series.{3}">{3}</a>, ' '<a href="search.{4}">{4}</a>.').format(_('Authors'), _('Tags'), _('Publishers'), _('Series'), _('Saved Searches'))) sl.setWordWrap(True) sl.setTextInteractionFlags(Qt.LinksAccessibleByMouse) sl.linkActivated.connect(self.link_activated) gl.addWidget(sl, 3, 0, 1, 2) gl.setRowStretch(3,10) self.hl = hl = QLabel(_(''' <h2>Virtual Libraries</h2> <p>Using <i>virtual libraries</i> you can restrict calibre to only show you books that match a search. When a virtual library is in effect, calibre behaves as though the library contains only the matched books. The Tag Browser display only the tags/authors/series/etc. that belong to the matched books and any searches you do will only search within the books in the virtual library. This is a good way to partition your large library into smaller and easier to work with subsets.</p> <p>For example you can use a Virtual Library to only show you books with the Tag <i>"Unread"</i> or only books by <i>"My Favorite Author"</i> or only books in a particular series.</p> <p>More information and examples are available in the <a href="%s">User Manual</a>.</p> ''') % localize_user_manual_link('http://manual.calibre-ebook.com/virtual_libraries.html')) hl.setWordWrap(True) hl.setOpenExternalLinks(True) hl.setFrameStyle(hl.StyledPanel) gl.addWidget(hl, 0, 3, 4, 1) bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) gl.addWidget(bb, 4, 0, 1, 0) if editing: db = self.gui.current_db virt_libs = db.prefs.get('virtual_libraries', {}) for dex,vl in enumerate(sorted(virt_libs.keys(), key=sort_key)): self.vl_name.addItem(vl, virt_libs.get(vl, '')) if vl == editing: self.vl_name.setCurrentIndex(dex) self.original_index = dex self.original_search = virt_libs.get(editing, '') self.vl_text.setText(self.original_search) self.new_name = editing self.vl_name.currentIndexChanged[int].connect(self.name_index_changed) self.vl_name.lineEdit().textEdited.connect(self.name_text_edited) self.resize(self.sizeHint()+QSize(150, 25))
class ExtraCustomization(DeviceConfigTab): # {{{ def __init__(self, extra_customization_message, extra_customization_choices, device_settings): super(ExtraCustomization, self).__init__() debug_print( "ExtraCustomization.__init__ - extra_customization_message=", extra_customization_message) debug_print( "ExtraCustomization.__init__ - extra_customization_choices=", extra_customization_choices) debug_print( "ExtraCustomization.__init__ - device_settings.extra_customization=", device_settings.extra_customization) debug_print("ExtraCustomization.__init__ - device_settings=", device_settings) self.extra_customization_message = extra_customization_message self.l = QVBoxLayout(self) self.setLayout(self.l) options_group = QGroupBox(_("Extra driver customization options"), self) self.l.addWidget(options_group) self.extra_layout = QGridLayout() self.extra_layout.setObjectName("extra_layout") options_group.setLayout(self.extra_layout) if extra_customization_message: extra_customization_choices = extra_customization_choices or {} def parse_msg(m): msg, _, tt = m.partition(':::') if m else ('', '', '') return msg.strip(), textwrap.fill(tt.strip(), 100) if isinstance(extra_customization_message, list): self.opt_extra_customization = [] if len(extra_customization_message) > 6: row_func = lambda x, y: ((x / 2) * 2) + y col_func = lambda x: x % 2 else: row_func = lambda x, y: x * 2 + y col_func = lambda x: 0 for i, m in enumerate(extra_customization_message): label_text, tt = parse_msg(m) if not label_text: self.opt_extra_customization.append(None) continue if isinstance(device_settings.extra_customization[i], bool): self.opt_extra_customization.append( QCheckBox(label_text)) self.opt_extra_customization[-1].setToolTip(tt) self.opt_extra_customization[i].setChecked( bool(device_settings.extra_customization[i])) elif i in extra_customization_choices: cb = QComboBox(self) self.opt_extra_customization.append(cb) l = QLabel(label_text) l.setToolTip(tt), cb.setToolTip(tt), l.setBuddy( cb), cb.setToolTip(tt) for li in sorted(extra_customization_choices[i]): self.opt_extra_customization[i].addItem(li) cb.setCurrentIndex( max( 0, cb.findText( device_settings.extra_customization[i]))) else: self.opt_extra_customization.append(QLineEdit(self)) l = QLabel(label_text) l.setToolTip(tt) self.opt_extra_customization[i].setToolTip(tt) l.setBuddy(self.opt_extra_customization[i]) l.setWordWrap(True) self.opt_extra_customization[i].setText( device_settings.extra_customization[i]) self.opt_extra_customization[i].setCursorPosition(0) self.extra_layout.addWidget(l, row_func(i + 2, 0), col_func(i)) self.extra_layout.addWidget( self.opt_extra_customization[i], row_func(i + 2, 1), col_func(i)) spacerItem1 = QSpacerItem(10, 10, QSizePolicy.Minimum, QSizePolicy.Expanding) self.extra_layout.addItem(spacerItem1, row_func(i + 2 + 2, 1), 0, 1, 2) self.extra_layout.setRowStretch(row_func(i + 2 + 2, 1), 2) else: self.opt_extra_customization = QLineEdit() label_text, tt = parse_msg(extra_customization_message) l = QLabel(label_text) l.setToolTip(tt) l.setBuddy(self.opt_extra_customization) l.setWordWrap(True) if device_settings.extra_customization: self.opt_extra_customization.setText( device_settings.extra_customization) self.opt_extra_customization.setCursorPosition(0) self.opt_extra_customization.setCursorPosition(0) self.extra_layout.addWidget(l, 0, 0) self.extra_layout.addWidget(self.opt_extra_customization, 1, 0) def extra_customization(self): ec = [] if self.extra_customization_message: if isinstance(self.extra_customization_message, list): for i in range(0, len(self.extra_customization_message)): if self.opt_extra_customization[i] is None: ec.append(None) continue if hasattr(self.opt_extra_customization[i], 'isChecked'): ec.append(self.opt_extra_customization[i].isChecked()) elif hasattr(self.opt_extra_customization[i], 'currentText'): ec.append( unicode(self.opt_extra_customization[i]. currentText()).strip()) else: ec.append( unicode(self.opt_extra_customization[i].text()). strip()) else: ec = unicode(self.opt_extra_customization.text()).strip() if not ec: ec = None return ec @property def has_extra_customizations(self): debug_print( "ExtraCustomization::has_extra_customizations - self.extra_customization_message", self.extra_customization_message) return self.extra_customization_message and len( self.extra_customization_message) > 0