예제 #1
0
class LoadingOverlay(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.l = l = QVBoxLayout(self)
        self.pi = ProgressIndicator(self, 96)
        self.setVisible(False)
        self.label = QLabel(self)
        self.label.setText(
            '<i>testing with some long and wrap worthy message that should hopefully still render well'
        )
        self.label.setTextFormat(Qt.TextFormat.RichText)
        self.label.setAlignment(Qt.AlignmentFlag.AlignTop
                                | Qt.AlignmentFlag.AlignHCenter)
        self.label.setWordWrap(True)
        if parent is None:
            self.resize(300, 300)
        else:
            self.resize(parent.size())
        self.setAutoFillBackground(True)
        pal = self.palette()
        col = pal.color(QPalette.ColorRole.Window)
        col.setAlphaF(0.8)
        pal.setColor(QPalette.ColorRole.Window, col)
        self.setPalette(pal)
        self.move(0, 0)
        f = self.font()
        f.setBold(True)
        fm = QFontInfo(f)
        f.setPixelSize(int(fm.pixelSize() * 1.5))
        self.label.setFont(f)
        l.addStretch(10)
        l.addWidget(self.pi)
        l.addWidget(self.label)
        l.addStretch(10)

    def __call__(self, msg=''):
        self.label.setText(msg)
        self.resize(self.parent().size())
        self.move(0, 0)
        self.setVisible(True)
        self.raise_()
        self.setFocus(Qt.FocusReason.OtherFocusReason)
        self.update()

    def hide(self):
        self.parent().web_view.setFocus(Qt.FocusReason.OtherFocusReason)
        self.pi.stop()
        return QWidget.hide(self)

    def showEvent(self, ev):
        # import time
        # self.st = time.monotonic()
        self.pi.start()

    def hideEvent(self, ev):
        # import time
        # print(1111111, time.monotonic() - self.st)
        self.pi.stop()
예제 #2
0
 def create_template_widget(title, which, button):
     attr = which + '_template'
     heading = QLabel('<h2>' + title)
     setattr(tp, attr + '_heading', heading)
     l.addWidget(heading)
     la = QLabel()
     setattr(self, attr, la)
     l.addWidget(la), la.setTextFormat(Qt.TextFormat.PlainText), la.setStyleSheet('QLabel {font-family: monospace}')
     la.setWordWrap(True)
     b = QPushButton(button)
     b.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
     connect_lambda(b.clicked, self, lambda self: self.change_template(which))
     setattr(self, attr + '_button', b)
     l.addWidget(b)
     if which != 'footer':
         f = QFrame(tp)
         setattr(tp, attr + '_sep', f), f.setFrameShape(QFrame.Shape.HLine)
         l.addWidget(f)
     l.addSpacing(10)
예제 #3
0
class JobError(QDialog):  # {{{

    WIDTH = 600
    do_pop = pyqtSignal()

    def __init__(self, parent):
        QDialog.__init__(self, parent)
        self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, False)
        self.queue = []
        self.do_pop.connect(self.pop, type=Qt.ConnectionType.QueuedConnection)

        self._layout = l = QGridLayout()
        self.setLayout(l)
        self.icon = QIcon(I('dialog_error.png'))
        self.setWindowIcon(self.icon)
        self.icon_widget = Icon(self)
        self.icon_widget.set_icon(self.icon)
        self.msg_label = QLabel('<p>&nbsp;')
        self.msg_label.setStyleSheet('QLabel { margin-top: 1ex; }')
        self.msg_label.setWordWrap(True)
        self.msg_label.setTextFormat(Qt.TextFormat.RichText)
        self.det_msg = QPlainTextEdit(self)
        self.det_msg.setVisible(False)

        self.bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Close,
                                   parent=self)
        self.bb.accepted.connect(self.accept)
        self.bb.rejected.connect(self.reject)
        self.ctc_button = self.bb.addButton(
            _('&Copy to clipboard'), QDialogButtonBox.ButtonRole.ActionRole)
        self.ctc_button.clicked.connect(self.copy_to_clipboard)
        self.retry_button = self.bb.addButton(
            _('&Retry'), QDialogButtonBox.ButtonRole.ActionRole)
        self.retry_button.clicked.connect(self.retry)
        self.retry_func = None
        self.show_det_msg = _('Show &details')
        self.hide_det_msg = _('Hide &details')
        self.det_msg_toggle = self.bb.addButton(
            self.show_det_msg, QDialogButtonBox.ButtonRole.ActionRole)
        self.det_msg_toggle.clicked.connect(self.toggle_det_msg)
        self.det_msg_toggle.setToolTip(
            _('Show detailed information about this error'))
        self.suppress = QCheckBox(self)

        l.addWidget(self.icon_widget, 0, 0, 1, 1)
        l.addWidget(self.msg_label, 0, 1, 1, 1)
        l.addWidget(self.det_msg, 1, 0, 1, 2)
        l.addWidget(self.suppress, 2, 0, 1, 2,
                    Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignBottom)
        l.addWidget(self.bb, 3, 0, 1, 2,
                    Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignBottom)
        l.setColumnStretch(1, 100)

        self.setModal(False)
        self.suppress.setVisible(False)
        self.do_resize()

    def retry(self):
        if self.retry_func is not None:
            self.accept()
            self.retry_func()

    def update_suppress_state(self):
        self.suppress.setText(
            ngettext('Hide the remaining error message',
                     'Hide the {} remaining error messages',
                     len(self.queue)).format(len(self.queue)))
        self.suppress.setVisible(len(self.queue) > 3)
        self.do_resize()

    def copy_to_clipboard(self, *args):
        d = QTextDocument()
        d.setHtml(self.msg_label.text())
        QApplication.clipboard().setText(
            'calibre, version %s (%s, embedded-python: %s)\n%s: %s\n\n%s' %
            (__version__, sys.platform, isfrozen, str(self.windowTitle()),
             str(d.toPlainText()), str(self.det_msg.toPlainText())))
        if hasattr(self, 'ctc_button'):
            self.ctc_button.setText(_('Copied'))

    def toggle_det_msg(self, *args):
        vis = str(self.det_msg_toggle.text()) == self.hide_det_msg
        self.det_msg_toggle.setText(
            self.show_det_msg if vis else self.hide_det_msg)
        self.det_msg.setVisible(not vis)
        self.do_resize()

    def do_resize(self):
        h = self.sizeHint().height()
        self.setMinimumHeight(0)  # Needed as this gets set if det_msg is shown
        # Needed otherwise re-showing the box after showing det_msg causes the box
        # to not reduce in height
        self.setMaximumHeight(h)
        self.resize(QSize(self.WIDTH, h))

    def showEvent(self, ev):
        ret = QDialog.showEvent(self, ev)
        self.bb.button(QDialogButtonBox.StandardButton.Close).setFocus(
            Qt.FocusReason.OtherFocusReason)
        return ret

    def show_error(self, title, msg, det_msg='', retry_func=None):
        self.queue.append((title, msg, det_msg, retry_func))
        self.update_suppress_state()
        self.pop()

    def pop(self):
        if not self.queue or self.isVisible():
            return
        title, msg, det_msg, retry_func = self.queue.pop(0)
        self.setWindowTitle(title)
        self.msg_label.setText(msg)
        self.det_msg.setPlainText(det_msg)
        self.det_msg.setVisible(False)
        self.det_msg_toggle.setText(self.show_det_msg)
        self.det_msg_toggle.setVisible(True)
        self.suppress.setChecked(False)
        self.update_suppress_state()
        if not det_msg:
            self.det_msg_toggle.setVisible(False)
        self.retry_button.setVisible(retry_func is not None)
        self.retry_func = retry_func
        self.do_resize()
        self.show()

    def done(self, r):
        if self.suppress.isChecked():
            self.queue = []
        QDialog.done(self, r)
        self.do_pop.emit()
예제 #4
0
class IdentifyWidget(QWidget):  # {{{

    rejected = pyqtSignal()
    results_found = pyqtSignal()
    book_selected = pyqtSignal(object, object)

    def __init__(self, log, parent=None):
        QWidget.__init__(self, parent)
        self.log = log
        self.abort = Event()
        self.caches = {}

        self.l = l = QVBoxLayout(self)

        names = [
            '<b>' + p.name + '</b>' for p in metadata_plugins(['identify'])
            if p.is_configured()
        ]
        self.top = QLabel('<p>' + _('calibre is downloading metadata from: ') +
                          ', '.join(names))
        self.top.setWordWrap(True)
        l.addWidget(self.top)

        self.splitter = s = QSplitter(self)
        s.setChildrenCollapsible(False)
        l.addWidget(s, 100)
        self.results_view = ResultsView(self)
        self.results_view.book_selected.connect(self.emit_book_selected)
        self.get_result = self.results_view.get_result
        s.addWidget(self.results_view)

        self.comments_view = Comments(self)
        s.addWidget(self.comments_view)
        s.setStretchFactor(0, 2)
        s.setStretchFactor(1, 1)

        self.results_view.show_details_signal.connect(
            self.comments_view.show_data)

        self.query = QLabel('download starting...')
        self.query.setWordWrap(True)
        self.query.setTextFormat(Qt.TextFormat.PlainText)
        l.addWidget(self.query)

        self.comments_view.show_wait()
        state = gprefs.get('metadata-download-identify-widget-splitter-state')
        if state is not None:
            s.restoreState(state)

    def save_state(self):
        gprefs['metadata-download-identify-widget-splitter-state'] = bytearray(
            self.splitter.saveState())

    def emit_book_selected(self, book):
        self.book_selected.emit(book, self.caches)

    def start(self, title=None, authors=None, identifiers={}):
        self.log.clear()
        self.log('Starting download')
        parts, simple_desc = [], ''
        if title:
            parts.append('title:' + title)
            simple_desc += _('Title: %s ') % title
        if authors:
            parts.append('authors:' + authors_to_string(authors))
            simple_desc += _('Authors: %s ') % authors_to_string(authors)
        if identifiers:
            x = ', '.join('%s:%s' % (k, v) for k, v in iteritems(identifiers))
            parts.append(x)
            if 'isbn' in identifiers:
                simple_desc += 'ISBN: %s' % identifiers['isbn']
        self.query.setText(simple_desc)
        self.log(str(self.query.text()))

        self.worker = IdentifyWorker(self.log, self.abort, title, authors,
                                     identifiers, self.caches)

        self.worker.start()

        QTimer.singleShot(50, self.update)

    def update(self):
        if self.worker.is_alive():
            QTimer.singleShot(50, self.update)
        else:
            self.process_results()

    def process_results(self):
        if self.worker.error is not None:
            error_dialog(self,
                         _('Download failed'),
                         _('Failed to download metadata. Click '
                           'Show Details to see details'),
                         show=True,
                         det_msg=self.worker.error)
            self.rejected.emit()
            return

        if not self.worker.results:
            log = ''.join(self.log.plain_text)
            error_dialog(
                self,
                _('No matches found'),
                '<p>' +
                _('Failed to find any books that '
                  'match your search. Try making the search <b>less '
                  'specific</b>. For example, use only the author\'s '
                  'last name and a single distinctive word from '
                  'the title.<p>To see the full log, click "Show details".'),
                show=True,
                det_msg=log)
            self.rejected.emit()
            return

        self.results_view.show_results(self.worker.results)
        self.results_found.emit()

    def cancel(self):
        self.abort.set()
예제 #5
0
    def __init__(self, db, book_id_map, parent=None):
        from calibre.ebooks.oeb.polish.main import HELP
        QDialog.__init__(self, parent)
        self.db, self.book_id_map = weakref.ref(db), book_id_map
        self.setWindowIcon(QIcon(I('polish.png')))
        title = _('Polish book')
        if len(book_id_map) > 1:
            title = _('Polish %d books') % len(book_id_map)
        self.setWindowTitle(title)

        self.help_text = {
            'polish':
            _('<h3>About Polishing books</h3>%s') % HELP['about'].format(
                _('''<p>If you have both EPUB and ORIGINAL_EPUB in your book,
                  then polishing will run on ORIGINAL_EPUB (the same for other
                  ORIGINAL_* formats).  So if you
                  want Polishing to not run on the ORIGINAL_* format, delete the
                  ORIGINAL_* format before running it.</p>''')),
            'embed':
            _('<h3>Embed referenced fonts</h3>%s') % HELP['embed'],
            'subset':
            _('<h3>Subsetting fonts</h3>%s') % HELP['subset'],
            'smarten_punctuation':
            _('<h3>Smarten punctuation</h3>%s') % HELP['smarten_punctuation'],
            'metadata':
            _('<h3>Updating metadata</h3>'
              '<p>This will update all metadata <i>except</i> the cover in the'
              ' e-book files to match the current metadata in the'
              ' calibre library.</p>'
              ' <p>Note that most e-book'
              ' formats are not capable of supporting all the'
              ' metadata in calibre.</p><p>There is a separate option to'
              ' update the cover.</p>'),
            'do_cover':
            _('<h3>Update cover</h3><p>Update the covers in the e-book files to match the'
              ' current cover in the calibre library.</p>'
              '<p>If the e-book file does not have'
              ' an identifiable cover, a new cover is inserted.</p>'),
            'jacket':
            _('<h3>Book jacket</h3>%s') % HELP['jacket'],
            'remove_jacket':
            _('<h3>Remove book jacket</h3>%s') % HELP['remove_jacket'],
            'remove_unused_css':
            _('<h3>Remove unused CSS rules</h3>%s') %
            HELP['remove_unused_css'],
            'compress_images':
            _('<h3>Losslessly compress images</h3>%s') %
            HELP['compress_images'],
            'add_soft_hyphens':
            _('<h3>Add soft-hyphens</h3>%s') % HELP['add_soft_hyphens'],
            'remove_soft_hyphens':
            _('<h3>Remove soft-hyphens</h3>%s') % HELP['remove_soft_hyphens'],
            'upgrade_book':
            _('<h3>Upgrade book internals</h3>%s') % HELP['upgrade_book'],
        }

        self.l = l = QGridLayout()
        self.setLayout(l)

        self.la = la = QLabel('<b>' + _('Select actions to perform:'))
        l.addWidget(la, 0, 0, 1, 2)

        count = 0
        self.all_actions = OrderedDict([
            ('embed', _('&Embed all referenced fonts')),
            ('subset', _('&Subset all embedded fonts')),
            ('smarten_punctuation', _('Smarten &punctuation')),
            ('metadata', _('Update &metadata in the book files')),
            ('do_cover', _('Update the &cover in the book files')),
            ('jacket', _('Add/replace metadata as a "book &jacket" page')),
            ('remove_jacket', _('&Remove a previously inserted book jacket')),
            ('remove_unused_css', _('Remove &unused CSS rules from the book')),
            ('compress_images', _('Losslessly &compress images')),
            ('add_soft_hyphens', _('Add s&oft hyphens')),
            ('remove_soft_hyphens', _('Remove so&ft hyphens')),
            ('upgrade_book', _('&Upgrade book internals')),
        ])
        prefs = gprefs.get('polishing_settings', {})
        for name, text in iteritems(self.all_actions):
            count += 1
            x = QCheckBox(text, self)
            x.setChecked(prefs.get(name, False))
            x.setObjectName(name)
            connect_lambda(
                x.stateChanged, self, lambda self, state: self.option_toggled(
                    self.sender().objectName(), state))
            l.addWidget(x, count, 0, 1, 1)
            setattr(self, 'opt_' + name, x)
            la = QLabel(' <a href="#%s">%s</a>' % (name, _('About')))
            setattr(self, 'label_' + name, x)
            la.linkActivated.connect(self.help_link_activated)
            l.addWidget(la, count, 1, 1, 1)

        count += 1
        l.addItem(QSpacerItem(10, 10, vPolicy=QSizePolicy.Policy.Expanding),
                  count, 1, 1, 2)

        la = self.help_label = QLabel('')
        self.help_link_activated('#polish')
        la.setWordWrap(True)
        la.setTextFormat(Qt.TextFormat.RichText)
        la.setFrameShape(QFrame.Shape.StyledPanel)
        la.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
        la.setLineWidth(2)
        la.setStyleSheet('QLabel { margin-left: 75px }')
        l.addWidget(la, 0, 2, count + 1, 1)
        l.setColumnStretch(2, 1)

        self.show_reports = sr = QCheckBox(_('Show &report'), self)
        sr.setChecked(gprefs.get('polish_show_reports', True))
        sr.setToolTip(
            textwrap.fill(
                _('Show a report of all the actions performed'
                  ' after polishing is completed')))
        l.addWidget(sr, count + 1, 0, 1, 1)
        self.bb = bb = QDialogButtonBox(
            QDialogButtonBox.StandardButton.Ok
            | QDialogButtonBox.StandardButton.Cancel)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        self.save_button = sb = bb.addButton(
            _('&Save settings'), QDialogButtonBox.ButtonRole.ActionRole)
        sb.clicked.connect(self.save_settings)
        self.load_button = lb = bb.addButton(
            _('&Load settings'), QDialogButtonBox.ButtonRole.ActionRole)
        self.load_menu = QMenu(lb)
        lb.setMenu(self.load_menu)
        self.all_button = b = bb.addButton(
            _('Select &all'), QDialogButtonBox.ButtonRole.ActionRole)
        connect_lambda(b.clicked, self, lambda self: self.select_all(True))
        self.none_button = b = bb.addButton(
            _('Select &none'), QDialogButtonBox.ButtonRole.ActionRole)
        connect_lambda(b.clicked, self, lambda self: self.select_all(False))
        l.addWidget(bb, count + 1, 1, 1, -1)
        self.setup_load_button()

        self.resize(QSize(950, 600))