Exemple #1
0
class ProgressIndicator(QWidget):  # {{{
    def __init__(self, *args):
        QWidget.__init__(self, *args)
        self.setGeometry(0, 0, 300, 350)
        self.pi = _ProgressIndicator(self)
        self.status = QLabel(self)
        self.status.setWordWrap(True)
        self.status.setAlignment(Qt.AlignmentFlag.AlignHCenter
                                 | Qt.AlignmentFlag.AlignTop)
        self.setVisible(False)
        self.pos = None

    def start(self, msg=''):
        view = self.parent()
        pwidth, pheight = view.size().width(), view.size().height()
        self.resize(pwidth, min(pheight, 250))
        if self.pos is None:
            self.move(0, int((pheight - self.size().height()) / 2))
        else:
            self.move(self.pos[0], self.pos[1])
        self.pi.resize(self.pi.sizeHint())
        self.pi.move(int((self.size().width() - self.pi.size().width()) / 2),
                     0)
        self.status.resize(self.size().width(),
                           self.size().height() - self.pi.size().height() - 10)
        self.status.move(0, self.pi.size().height() + 10)
        self.status.setText('<h1>' + msg + '</h1>')
        self.setVisible(True)
        self.pi.startAnimation()

    def stop(self):
        self.pi.stopAnimation()
        self.setVisible(False)
Exemple #2
0
 def setup_ui(self):
     self.l = l = QFormLayout(self)
     l.setFieldGrowthPolicy(
         QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
     l.addRow(
         QLabel(
             _('The key of the identifier, for example, in isbn:XXX, the key is "isbn"'
               )))
     self.key = k = QLineEdit(self)
     l.addRow(_('&Key:'), k)
     l.addRow(
         QLabel(_('The name that will appear in the Book details panel')))
     self.nw = n = QLineEdit(self)
     l.addRow(_('&Name:'), n)
     la = QLabel(
         _('The template used to create the link.'
           ' The placeholder {0} in the template will be replaced'
           ' with the actual identifier value. Use {1} to avoid the value'
           ' being quoted.').format('{id}', '{id_unquoted}'))
     la.setWordWrap(True)
     l.addRow(la)
     self.template = t = QLineEdit(self)
     l.addRow(_('&Template:'), t)
     t.selectAll()
     t.setFocus(Qt.FocusReason.OtherFocusReason)
     l.addWidget(self.bb)
Exemple #3
0
    def setup_ui(self):
        self.l = l = QGridLayout()
        self.setLayout(l)

        self.la = la = QLabel(_(
            'Arrange the files in this book into sub-folders based on their types.'
            ' If you leave a folder blank, the files will be placed in the root.'))
        la.setWordWrap(True)
        l.addWidget(la, 0, 0, 1, -1)

        folders = tprefs['folders_for_types']
        for i, (typ, text) in enumerate(self.TYPE_MAP):
            la = QLabel('&' + text)
            setattr(self, '%s_label' % typ, la)
            le = QLineEdit(self)
            setattr(self, '%s_folder' % typ, le)
            val = folders.get(typ, '')
            if val and not val.endswith('/'):
                val += '/'
            le.setText(val)
            la.setBuddy(le)
            l.addWidget(la, i + 1, 0)
            l.addWidget(le, i + 1, 1)
        self.la2 = la = QLabel(_(
            'Note that this will only arrange files inside the book,'
            ' it will not affect how they are displayed in the File browser'))
        la.setWordWrap(True)
        l.addWidget(la, i + 2, 0, 1, -1)
        l.addWidget(self.bb, i + 3, 0, 1, -1)
Exemple #4
0
class MovedDialog(QDialog):  # {{{
    def __init__(self, stats, location, parent=None):
        QDialog.__init__(self, parent)
        self.setWindowTitle(_('No library found'))
        self._l = l = QGridLayout(self)
        self.setLayout(l)
        self.stats, self.location = stats, location

        loc = self.oldloc = location.replace('/', os.sep)
        self.header = QLabel(
            _('No existing calibre library was found at %s. '
              'If the library was moved, select its new location below. '
              'Otherwise calibre will forget this library.') % loc)
        self.header.setWordWrap(True)
        ncols = 2
        l.addWidget(self.header, 0, 0, 1, ncols)
        self.cl = QLabel('<b>' + _('New location of this library:'))
        l.addWidget(self.cl, l.rowCount(), 0, 1, ncols)
        self.loc = QLineEdit(loc, self)
        l.addWidget(self.loc, l.rowCount(), 0, 1, 1)
        self.cd = QToolButton(self)
        self.cd.setIcon(QIcon(I('document_open.png')))
        self.cd.clicked.connect(self.choose_dir)
        l.addWidget(self.cd, l.rowCount() - 1, 1, 1, 1)
        self.bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Abort)
        b = self.bb.addButton(_('Library moved'),
                              QDialogButtonBox.ButtonRole.AcceptRole)
        b.setIcon(QIcon(I('ok.png')))
        b = self.bb.addButton(_('Forget library'),
                              QDialogButtonBox.ButtonRole.RejectRole)
        b.setIcon(QIcon(I('edit-clear.png')))
        b.clicked.connect(self.forget_library)
        self.bb.accepted.connect(self.accept)
        self.bb.rejected.connect(self.reject)
        l.addWidget(self.bb, 3, 0, 1, ncols)
        self.resize(self.sizeHint() + QSize(120, 0))

    def choose_dir(self):
        d = choose_dir(self,
                       'library moved choose new loc',
                       _('New library location'),
                       default_dir=self.oldloc)
        if d is not None:
            self.loc.setText(d)

    def forget_library(self):
        self.stats.remove(self.location)

    def accept(self):
        newloc = str(self.loc.text())
        if not db_class().exists_at(newloc):
            error_dialog(self,
                         _('No library found'),
                         _('No existing calibre library found at %s') % newloc,
                         show=True)
            return
        self.stats.rename(self.location, newloc)
        self.newloc = newloc
        QDialog.accept(self)
Exemple #5
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()
Exemple #6
0
    def do_user_config(self, parent=None):
        '''
        This method shows a configuration dialog for this plugin. It returns
        True if the user clicks OK, False otherwise. The changes are
        automatically applied.
        '''
        from qt.core import (QDialog, QDialogButtonBox, QVBoxLayout, QLabel,
                             Qt, QLineEdit, QCheckBox)

        config_dialog = QDialog(parent)
        button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok
                                      | QDialogButtonBox.StandardButton.Cancel)
        v = QVBoxLayout(config_dialog)

        def size_dialog():
            config_dialog.resize(config_dialog.sizeHint())

        button_box.accepted.connect(config_dialog.accept)
        button_box.rejected.connect(config_dialog.reject)
        config_dialog.setWindowTitle(_('Customize') + ' ' + self.name)
        from calibre.customize.ui import (plugin_customization,
                                          customize_plugin)
        help_text = self.customization_help(gui=True)
        help_text = QLabel(help_text, config_dialog)
        help_text.setWordWrap(True)
        help_text.setTextInteractionFlags(
            Qt.TextInteractionFlag.LinksAccessibleByMouse
            | Qt.TextInteractionFlag.LinksAccessibleByKeyboard)
        help_text.setOpenExternalLinks(True)
        v.addWidget(help_text)
        bf = QCheckBox(_('Add linked files in breadth first order'))
        bf.setToolTip(
            _('Normally, when following links in HTML files'
              ' calibre does it depth first, i.e. if file A links to B and '
              ' C, but B links to D, the files are added in the order A, B, D, C. '
              ' With this option, they will instead be added as A, B, C, D'))
        sc = plugin_customization(self)
        if not sc:
            sc = ''
        sc = sc.strip()
        enc = sc.partition('|')[0]
        bfs = sc.partition('|')[-1]
        bf.setChecked(bfs == 'bf')
        sc = QLineEdit(enc, config_dialog)
        v.addWidget(sc)
        v.addWidget(bf)
        v.addWidget(button_box)
        size_dialog()
        config_dialog.exec()

        if config_dialog.result() == QDialog.DialogCode.Accepted:
            sc = str(sc.text()).strip()
            if bf.isChecked():
                sc += '|bf'
            customize_plugin(self, sc)

        return config_dialog.result()
Exemple #7
0
 def add_row(x, y=None):
     if isinstance(x, unicode_type):
         x = QLabel(x)
     row = l.rowCount()
     if y is None:
         if isinstance(x, QLabel):
             x.setWordWrap(True)
         l.addWidget(x, row, 0, 1, 2)
     else:
         if isinstance(x, QLabel):
             x.setBuddy(y)
         l.addWidget(x, row, 0), l.addWidget(y, row, 1)
Exemple #8
0
    def __init__(self, ids, parent):
        QDialog.__init__(self, parent)
        self.setWindowTitle(_('Schedule download?'))
        self.setWindowIcon(QIcon(I('download-metadata.png')))

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

        i = QLabel(self)
        i.setPixmap(QIcon(I('download-metadata.png')).pixmap(128, 128))
        l.addWidget(i, 0, 0)
        t = ngettext(
            'The download of metadata for the <b>selected book</b> will run in the background. Proceed?',
            'The download of metadata for the <b>{} selected books</b> will run in the background. Proceed?',
            len(ids)).format(len(ids))

        t = QLabel(
            '<p>' + t + '<p>' +
            _('You can monitor the progress of the download '
              'by clicking the rotating spinner in the bottom right '
              'corner.') + '<p>' +
            _('When the download completes you will be asked for'
              ' confirmation before calibre applies the downloaded metadata.'))
        t.setWordWrap(True)
        l.addWidget(t, 0, 1)
        l.setColumnStretch(0, 1)
        l.setColumnStretch(1, 100)

        self.identify = self.covers = True
        self.bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Cancel)
        self.bb.rejected.connect(self.reject)
        b = self.bb.addButton(_('Download only &metadata'),
                              QDialogButtonBox.ButtonRole.AcceptRole)
        b.clicked.connect(self.only_metadata)
        b.setIcon(QIcon(I('edit_input.png')))
        b = self.bb.addButton(_('Download only &covers'),
                              QDialogButtonBox.ButtonRole.AcceptRole)
        b.clicked.connect(self.only_covers)
        b.setIcon(QIcon(I('default_cover.png')))
        b = self.b = self.bb.addButton(_('&Configure download'),
                                       QDialogButtonBox.ButtonRole.ActionRole)
        b.setIcon(QIcon(I('config.png')))
        connect_lambda(b.clicked, self, lambda self: show_config(self))
        l.addWidget(self.bb, 1, 0, 1, 2)
        b = self.bb.addButton(_('Download &both'),
                              QDialogButtonBox.ButtonRole.AcceptRole)
        b.clicked.connect(self.accept)
        b.setDefault(True)
        b.setAutoDefault(True)
        b.setIcon(QIcon(I('ok.png')))

        self.resize(self.sizeHint())
        b.setFocus(Qt.FocusReason.OtherFocusReason)
Exemple #9
0
class DBCheck(QDialog):  # {{{

    update_msg = pyqtSignal(object)

    def __init__(self, parent, db):
        QDialog.__init__(self, parent)
        self.l = QVBoxLayout()
        self.setLayout(self.l)
        self.l1 = QLabel(
            _('Vacuuming database to improve performance.') + ' ' +
            _('This will take a while, please wait...'))
        self.setWindowTitle(_('Vacuuming...'))
        self.l1.setWordWrap(True)
        self.l.addWidget(self.l1)
        self.msg = QLabel('')
        self.update_msg.connect(self.msg.setText,
                                type=Qt.ConnectionType.QueuedConnection)
        self.l.addWidget(self.msg)
        self.msg.setWordWrap(True)
        self.resize(self.sizeHint() + QSize(100, 50))
        self.error = None
        self.db = db.new_api
        self.rejected = False

    def start(self):
        t = self.thread = Thread(target=self.vacuum)
        t.daemon = True
        t.start()
        QTimer.singleShot(100, self.check)
        self.exec_()

    def vacuum(self):
        try:
            self.db.vacuum()
        except Exception as e:
            import traceback
            self.error = (as_unicode(e), traceback.format_exc())

    def reject(self):
        self.rejected = True
        return QDialog.reject(self)

    def check(self):
        if self.rejected:
            return
        if self.thread.is_alive():
            QTimer.singleShot(100, self.check)
        else:
            self.accept()

    def break_cycles(self):
        self.db = self.thread = None
Exemple #10
0
    def view_server_logs(self):
        from calibre.srv.embedded import log_paths
        log_error_file, log_access_file = log_paths()
        d = QDialog(self)
        d.resize(QSize(800, 600))
        layout = QVBoxLayout()
        d.setLayout(layout)
        layout.addWidget(QLabel(_('Error log:')))
        el = QPlainTextEdit(d)
        layout.addWidget(el)
        try:
            el.setPlainText(
                share_open(log_error_file, 'rb').read().decode('utf8', 'replace')
            )
        except EnvironmentError:
            el.setPlainText(_('No error log found'))
        layout.addWidget(QLabel(_('Access log:')))
        al = QPlainTextEdit(d)
        layout.addWidget(al)
        try:
            al.setPlainText(
                share_open(log_access_file, 'rb').read().decode('utf8', 'replace')
            )
        except EnvironmentError:
            al.setPlainText(_('No access log found'))
        loc = QLabel(_('The server log files are in: {}').format(os.path.dirname(log_error_file)))
        loc.setWordWrap(True)
        layout.addWidget(loc)
        bx = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok)
        layout.addWidget(bx)
        bx.accepted.connect(d.accept)
        b = bx.addButton(_('&Clear logs'), QDialogButtonBox.ButtonRole.ActionRole)

        def clear_logs():
            if getattr(self.server, 'is_running', False):
                return error_dialog(d, _('Server running'), _(
                    'Cannot clear logs while the server is running. First stop the server.'), show=True)
            if self.server:
                self.server.access_log.clear()
                self.server.log.clear()
            else:
                for x in (log_error_file, log_access_file):
                    try:
                        os.remove(x)
                    except EnvironmentError as err:
                        if err.errno != errno.ENOENT:
                            raise
            el.setPlainText(''), al.setPlainText('')

        b.clicked.connect(clear_logs)
        d.show()
class SelectFormats(QDialog):
    def __init__(self,
                 fmt_count,
                 msg,
                 single=False,
                 parent=None,
                 exclude=False):
        QDialog.__init__(self, parent)
        self._l = QVBoxLayout(self)
        self.single_fmt = single
        self.setLayout(self._l)
        self.setWindowTitle(_('Choose formats'))
        self._m = QLabel(msg)
        self._m.setWordWrap(True)
        self._l.addWidget(self._m)
        self.formats = Formats(fmt_count)
        self.fview = QListView(self)
        self.fview.doubleClicked.connect(
            self.double_clicked, type=Qt.ConnectionType.QueuedConnection)
        if exclude:
            if QApplication.instance().is_dark_theme:
                sheet = 'background-color: #DAA520; color: black'
            else:
                sheet = 'background-color: #fae7b5'
            self.fview.setStyleSheet('QListView { %s }' % sheet)
        self._l.addWidget(self.fview)
        self.fview.setModel(self.formats)
        self.fview.setSelectionMode(
            QAbstractItemView.SelectionMode.SingleSelection
            if single else QAbstractItemView.SelectionMode.MultiSelection)
        self.bbox = \
        QDialogButtonBox(QDialogButtonBox.StandardButton.Ok|QDialogButtonBox.StandardButton.Cancel,
                Qt.Orientation.Horizontal, self)
        self._l.addWidget(self.bbox)
        self.bbox.accepted.connect(self.accept)
        self.bbox.rejected.connect(self.reject)
        self.fview.setIconSize(QSize(48, 48))
        self.fview.setSpacing(2)

        self.resize(350, 500)
        self.selected_formats = set()

    def accept(self, *args):
        for idx in self.fview.selectedIndexes():
            self.selected_formats.add(self.formats.fmt(idx))
        QDialog.accept(self, *args)

    def double_clicked(self, index):
        if self.single_fmt:
            self.accept()
Exemple #12
0
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.setFocusPolicy(Qt.FocusPolicy.NoFocus)
        self.l = l = QVBoxLayout(self)
        l.setContentsMargins(0, 0, 0, 0)
        self.search_input = si = SearchInput(self, 'highlights-search')
        si.do_search.connect(self.search_requested)
        l.addWidget(si)

        la = QLabel(_('Double click to jump to an entry'))
        la.setWordWrap(True)
        l.addWidget(la)

        self.highlights = h = Highlights(self)
        l.addWidget(h)
        h.jump_to_highlight.connect(self.jump_to_highlight)
        h.delete_requested.connect(self.remove_highlight)
        h.edit_requested.connect(self.edit_highlight)
        h.edit_notes_requested.connect(self.edit_notes)
        h.current_highlight_changed.connect(self.current_highlight_changed)
        self.load = h.load
        self.refresh = h.refresh

        self.h = h = QHBoxLayout()

        def button(icon, text, tt, target):
            b = QPushButton(QIcon(I(icon)), text, self)
            b.setToolTip(tt)
            b.setFocusPolicy(Qt.FocusPolicy.NoFocus)
            b.clicked.connect(target)
            return b

        self.edit_button = button('edit_input.png', _('Modify'),
                                  _('Modify the selected highlight'),
                                  self.edit_highlight)
        self.remove_button = button('trash.png', _('Delete'),
                                    _('Delete the selected highlights'),
                                    self.remove_highlight)
        self.export_button = button('save.png', _('Export'),
                                    _('Export all highlights'), self.export)
        h.addWidget(self.edit_button), h.addWidget(
            self.remove_button), h.addWidget(self.export_button)

        self.notes_display = nd = NotesDisplay(self)
        nd.notes_edited.connect(self.notes_edited)
        l.addWidget(nd)
        nd.setVisible(False)
        l.addLayout(h)
    def setup_ui(self):
        from calibre.gui2.tweak_book.templates import DEFAULT_TEMPLATES
        from calibre.gui2.tweak_book.editor.text import TextEdit
        # Cannot use QFormLayout as it does not play nice with TextEdit on windows
        self.l = l = QVBoxLayout(self)

        self.syntaxes = s = QComboBox(self)
        s.addItems(sorted(DEFAULT_TEMPLATES))
        s.setCurrentIndex(s.findText('html'))
        h = QHBoxLayout()
        l.addLayout(h)
        la = QLabel(_('Choose the &type of template to edit:'))
        la.setBuddy(s)
        h.addWidget(la), h.addWidget(s), h.addStretch(10)
        s.currentIndexChanged.connect(self.show_template)

        self.helpl = la = QLabel(
            _('The variables {0} and {1} will be replaced with the title and author of the book. {2}'
              ' is where the cursor will be positioned. If you want to include braces in your template,'
              ' for example for CSS rules, you have to escape them, like this: {3}'
              ).format(*[
                  '<code>%s</code>' % x for x in
                  ['{TITLE}', '{AUTHOR}', '%CURSOR%', 'body {{ color: red }}']
              ]))
        la.setWordWrap(True)
        l.addWidget(la)

        self.save_timer = t = QTimer(self)
        t.setSingleShot(True), t.setInterval(100)
        t.timeout.connect(self._save_syntax)

        self.editor = e = TextEdit(self)
        l.addWidget(e)
        e.textChanged.connect(self.save_syntax)

        self.show_template()

        self.bb.clear()
        self.bb.addButton(QDialogButtonBox.StandardButton.Close)
        self.rd = b = self.bb.addButton(
            QDialogButtonBox.StandardButton.RestoreDefaults)
        b.clicked.connect(self.restore_defaults)
        l.addWidget(self.bb)
Exemple #14
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)
Exemple #15
0
class BackupStatus(QDialog):  # {{{
    def __init__(self, gui):
        QDialog.__init__(self, gui)
        self.l = l = QVBoxLayout(self)
        self.msg = QLabel('')
        self.msg.setWordWrap(True)
        l.addWidget(self.msg)
        self.bb = bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Close)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        b = bb.addButton(_('Queue &all books for backup'),
                         QDialogButtonBox.ButtonRole.ActionRole)
        b.clicked.connect(self.mark_all_dirty)
        b.setIcon(QIcon(I('lt.png')))
        l.addWidget(bb)
        self.db = weakref.ref(gui.current_db)
        self.setResult(9)
        self.setWindowTitle(_('Backup status'))
        self.update()
        self.resize(self.sizeHint() + QSize(50, 15))

    def update(self):
        db = self.db()
        if db is None:
            return
        if self.result() != 9:
            return
        dirty_text = 'no'
        try:
            dirty_text = '%s' % db.dirty_queue_length()
        except:
            dirty_text = _('none')
        self.msg.setText('<p>' +
                         _('Book metadata files remaining to be written: %s') %
                         dirty_text)
        QTimer.singleShot(1000, self.update)

    def mark_all_dirty(self):
        db = self.db()
        if db is None:
            return
        db.new_api.mark_as_dirty(db.new_api.all_book_ids())
Exemple #16
0
class EveryXDays(Base):

    HELP = _('''\
                Download this periodical every x days. For example, if you
                choose 30 days, the periodical will be downloaded every 30
                days. Note that you can set periods of less than a day, like
                0.1 days to download a periodical more than once a day.
            ''')

    def __init__(self, parent=None):
        Base.__init__(self, parent)
        self.l1 = QLabel(_('&Download every:'))
        self.interval = QDoubleSpinBox(self)
        self.interval.setMinimum(0.04)
        self.interval.setSpecialValueText(_('every hour'))
        self.interval.setMaximum(1000.0)
        self.interval.setValue(31.0)
        self.interval.setSuffix(' ' + _('days'))
        self.interval.setSingleStep(1.0)
        self.interval.setDecimals(2)
        self.l1.setBuddy(self.interval)
        self.l2 = QLabel(
            _('Note: You can set intervals of less than a day,'
              ' by typing the value manually.'))
        self.l2.setWordWrap(True)

        self.l.addWidget(self.l1, 0, 0, 1, 1)
        self.l.addWidget(self.interval, 0, 1, 1, 1)
        self.l.addWidget(self.l2, 1, 0, 1, -1)

    def initialize(self, typ=None, val=None):
        if val is None:
            val = 31.0
        self.interval.setValue(val)

    @property
    def schedule(self):
        schedule = self.interval.value()
        return 'interval', schedule
Exemple #17
0
 def setup_ui(self):
     self.l = l = QFormLayout(self)
     self.name_edit = n = QLineEdit(self)
     n.setPlaceholderText(_('The name of the source'))
     n.setMinimumWidth(450)
     l.addRow(_('&Name:'), n)
     if self.initial_name:
         n.setText(self.initial_name)
         n.setReadOnly(True)
     self.url_edit = u = QLineEdit(self)
     u.setPlaceholderText(_('The URL template of the source'))
     u.setMinimumWidth(n.minimumWidth())
     l.addRow(_('&URL:'), u)
     if self.initial_url:
         u.setText(self.initial_url)
     la = QLabel(
         _('The URL template must starts with https:// and have {word} in it which will be replaced by the actual query'
           ))
     la.setWordWrap(True)
     l.addRow(la)
     l.addRow(self.bb)
     if self.initial_name:
         u.setFocus(Qt.FocusReason.OtherFocusReason)
Exemple #18
0
class PluginTweaks(QDialog):  # {{{

    def __init__(self, raw, parent=None):
        QDialog.__init__(self, parent)
        self.setWindowTitle(_('Plugin tweaks'))
        self.edit = QPlainTextEdit(self)
        self.highlighter = PythonHighlighter(self.edit.document())
        self.l = QVBoxLayout()
        self.setLayout(self.l)
        self.msg = QLabel(
            _('Add/edit tweaks for any custom plugins you have installed. '
                'Documentation for these tweaks should be available '
                'on the website from where you downloaded the plugins.'))
        self.msg.setWordWrap(True)
        self.l.addWidget(self.msg)
        self.l.addWidget(self.edit)
        self.edit.setPlainText(raw)
        self.bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok|QDialogButtonBox.StandardButton.Cancel,
                Qt.Orientation.Horizontal, self)
        self.bb.accepted.connect(self.accept)
        self.bb.rejected.connect(self.reject)
        self.l.addWidget(self.bb)
        self.resize(550, 300)
Exemple #19
0
class ConfigWidget(QWidget):
    def __init__(self, plugin):
        QWidget.__init__(self)
        self.plugin = plugin

        self.overl = l = QVBoxLayout(self)
        self.gb = QGroupBox(_('Metadata fields to download'), self)
        if plugin.config_help_message:
            self.pchm = QLabel(plugin.config_help_message)
            self.pchm.setWordWrap(True)
            self.pchm.setOpenExternalLinks(True)
            l.addWidget(self.pchm, 10)
        l.addWidget(self.gb)
        self.gb.l = g = QVBoxLayout(self.gb)
        g.setContentsMargins(0, 0, 0, 0)
        self.fields_view = v = FieldsList(self)
        g.addWidget(v)
        v.setFlow(QListView.Flow.LeftToRight)
        v.setWrapping(True)
        v.setResizeMode(QListView.ResizeMode.Adjust)
        self.fields_model = FieldsModel(self.plugin)
        self.fields_model.initialize()
        v.setModel(self.fields_model)
        self.memory = []
        self.widgets = []
        self.l = QGridLayout()
        self.l.setContentsMargins(0, 0, 0, 0)
        l.addLayout(self.l, 100)
        for opt in plugin.options:
            self.create_widgets(opt)

    def create_widgets(self, opt):
        val = self.plugin.prefs[opt.name]
        if opt.type == 'number':
            c = QSpinBox if isinstance(opt.default,
                                       numbers.Integral) else QDoubleSpinBox
            widget = c(self)
            widget.setValue(val)
        elif opt.type == 'string':
            widget = QLineEdit(self)
            widget.setText(val if val else '')
        elif opt.type == 'bool':
            widget = QCheckBox(opt.label, self)
            widget.setChecked(bool(val))
        elif opt.type == 'choices':
            widget = QComboBox(self)
            items = list(iteritems(opt.choices))
            items.sort(key=lambda k_v: sort_key(k_v[1]))
            for key, label in items:
                widget.addItem(label, (key))
            idx = widget.findData(val)
            widget.setCurrentIndex(idx)
        widget.opt = opt
        widget.setToolTip(textwrap.fill(opt.desc))
        self.widgets.append(widget)
        r = self.l.rowCount()
        if opt.type == 'bool':
            self.l.addWidget(widget, r, 0, 1, self.l.columnCount())
        else:
            l = QLabel(opt.label)
            l.setToolTip(widget.toolTip())
            self.memory.append(l)
            l.setBuddy(widget)
            self.l.addWidget(l, r, 0, 1, 1)
            self.l.addWidget(widget, r, 1, 1, 1)

    def commit(self):
        self.fields_model.commit()
        for w in self.widgets:
            if isinstance(w, (QSpinBox, QDoubleSpinBox)):
                val = w.value()
            elif isinstance(w, QLineEdit):
                val = str(w.text())
            elif isinstance(w, QCheckBox):
                val = w.isChecked()
            elif isinstance(w, QComboBox):
                idx = w.currentIndex()
                val = str(w.itemData(idx) or '')
            self.plugin.prefs[w.opt.name] = val
Exemple #20
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()
Exemple #21
0
    def __init__(self, parent):
        QFrame.__init__(self, parent)
        self.setFrameStyle(QFrame.Shape.NoFrame if gprefs['tag_browser_old_look'] else QFrame.Shape.StyledPanel)
        self._parent = parent
        self._layout = QVBoxLayout(self)
        self._layout.setContentsMargins(0,0,0,0)

        # Set up the find box & button
        self.tb_bar = tbb = TagBrowserBar(self)
        tbb.clear_find.connect(self.reset_find)
        self.alter_tb, self.item_search, self.search_button = tbb.alter_tb, tbb.item_search, tbb.search_button
        self.toggle_search_button = tbb.toggle_search_button
        self._layout.addWidget(tbb)

        self.current_find_position = None
        self.search_button.clicked.connect(self.find)
        self.item_search.lineEdit().textEdited.connect(self.find_text_changed)
        self.item_search.activated[str].connect(self.do_find)

        # The tags view
        parent.tags_view = TagsView(parent)
        self.tags_view = parent.tags_view
        self._layout.insertWidget(0, parent.tags_view)

        # Now the floating 'not found' box
        l = QLabel(self.tags_view)
        self.not_found_label = l
        l.setFrameStyle(QFrame.Shape.StyledPanel)
        l.setAutoFillBackground(True)
        l.setText('<p><b>'+_('No more matches.</b><p> Click Find again to go to first match'))
        l.setAlignment(Qt.AlignmentFlag.AlignVCenter)
        l.setWordWrap(True)
        l.resize(l.sizeHint())
        l.move(10,20)
        l.setVisible(False)
        self.not_found_label_timer = QTimer()
        self.not_found_label_timer.setSingleShot(True)
        self.not_found_label_timer.timeout.connect(self.not_found_label_timer_event,
                                                   type=Qt.ConnectionType.QueuedConnection)
        self.collapse_all_action = ac = QAction(parent)
        parent.addAction(ac)
        parent.keyboard.register_shortcut('tag browser collapse all',
                _('Collapse all'), default_keys=(),
                action=ac, group=_('Tag browser'))
        connect_lambda(ac.triggered, self, lambda self: self.tags_view.collapseAll())

        # The Configure Tag Browser button
        l = self.alter_tb
        ac = QAction(parent)
        parent.addAction(ac)
        parent.keyboard.register_shortcut('tag browser alter',
                _('Configure Tag browser'), default_keys=(),
                action=ac, group=_('Tag browser'))
        ac.triggered.connect(l.showMenu)

        l.m.aboutToShow.connect(self.about_to_show_configure_menu)
        l.m.show_counts_action = ac = l.m.addAction('counts')
        ac.triggered.connect(self.toggle_counts)
        l.m.show_avg_rating_action = ac = l.m.addAction('avg rating')
        ac.triggered.connect(self.toggle_avg_rating)
        sb = l.m.addAction(_('Sort by'))
        sb.m = l.sort_menu = QMenu(l.m)
        sb.setMenu(sb.m)
        sb.bg = QActionGroup(sb)

        # Must be in the same order as db2.CATEGORY_SORTS
        for i, x in enumerate((_('Name'), _('Number of books'),
                  _('Average rating'))):
            a = sb.m.addAction(x)
            sb.bg.addAction(a)
            a.setCheckable(True)
            if i == 0:
                a.setChecked(True)
        sb.setToolTip(
                _('Set the sort order for entries in the Tag browser'))
        sb.setStatusTip(sb.toolTip())

        ma = l.m.addAction(_('Search type when selecting multiple items'))
        ma.m = l.match_menu = QMenu(l.m)
        ma.setMenu(ma.m)
        ma.ag = QActionGroup(ma)

        # Must be in the same order as db2.MATCH_TYPE
        for i, x in enumerate((_('Match any of the items'), _('Match all of the items'))):
            a = ma.m.addAction(x)
            ma.ag.addAction(a)
            a.setCheckable(True)
            if i == 0:
                a.setChecked(True)
        ma.setToolTip(
                _('When selecting multiple entries in the Tag browser '
                    'match any or all of them'))
        ma.setStatusTip(ma.toolTip())

        mt = l.m.addAction(_('Manage authors, tags, etc.'))
        mt.setToolTip(_('All of these category_managers are available by right-clicking '
                       'on items in the Tag browser above'))
        mt.m = l.manage_menu = QMenu(l.m)
        mt.setMenu(mt.m)

        ac = QAction(parent)
        parent.addAction(ac)
        parent.keyboard.register_shortcut('tag browser toggle item',
                _("'Click' found item"), default_keys=(),
                action=ac, group=_('Tag browser'))
        ac.triggered.connect(self.toggle_item)

        ac = QAction(parent)
        parent.addAction(ac)
        parent.keyboard.register_shortcut('tag browser set focus',
                _("Give the Tag browser keyboard focus"), default_keys=(),
                action=ac, group=_('Tag browser'))
        ac.triggered.connect(self.give_tb_focus)
Exemple #22
0
 def label(text):
     la = QLabel(text)
     la.setWordWrap(True), l.addWidget(la), la.setMinimumWidth(450)
     l.addWidget(la)
     return la
Exemple #23
0
    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.Policy.Minimum,
                                          QSizePolicy.Policy.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)
Exemple #24
0
class UpdateNotification(QDialog):
    def __init__(self, calibre_version, plugin_updates, parent=None):
        QDialog.__init__(self, parent)
        self.setAttribute(Qt.WidgetAttribute.WA_QuitOnClose, False)
        self.resize(400, 250)
        self.l = QGridLayout()
        self.setLayout(self.l)
        self.logo = QLabel()
        self.logo.setMaximumWidth(110)
        self.logo.setPixmap(QIcon(I('lt.png')).pixmap(100, 100))
        ver = calibre_version
        if ver.endswith('.0'):
            ver = ver[:-2]
        self.label = QLabel(
            '<p>' +
            _('New version <b>{ver}</b> of {app} is available for download. '
              'See the <a href="{url}">new features</a>.').format(
                  url=localize_website_link(
                      'https://calibre-ebook.com/whats-new'),
                  app=__appname__,
                  ver=ver))
        self.label.setOpenExternalLinks(True)
        self.label.setWordWrap(True)
        self.setWindowTitle(_('Update available!'))
        self.setWindowIcon(QIcon(I('lt.png')))
        self.l.addWidget(self.logo, 0, 0)
        self.l.addWidget(self.label, 0, 1)
        self.cb = QCheckBox(_('Show this notification for future updates'),
                            self)
        self.l.addWidget(self.cb, 1, 0, 1, -1)
        self.cb.setChecked(config.get('new_version_notification'))
        self.cb.stateChanged.connect(self.show_future)
        self.bb = QDialogButtonBox(self)
        b = self.bb.addButton(_('&Get update'),
                              QDialogButtonBox.ButtonRole.AcceptRole)
        b.setDefault(True)
        b.setIcon(QIcon(I('arrow-down.png')))
        if plugin_updates > 0:
            b = self.bb.addButton(_('Update &plugins'),
                                  QDialogButtonBox.ButtonRole.ActionRole)
            b.setIcon(QIcon(I('plugins/plugin_updater.png')))
            b.clicked.connect(self.get_plugins,
                              type=Qt.ConnectionType.QueuedConnection)
        self.bb.addButton(QDialogButtonBox.StandardButton.Cancel)
        self.l.addWidget(self.bb, 2, 0, 1, -1)
        self.bb.accepted.connect(self.accept)
        self.bb.rejected.connect(self.reject)
        save_version_notified(calibre_version)

    def get_plugins(self):
        from calibre.gui2.dialogs.plugin_updater import (
            PluginUpdaterDialog, FILTER_UPDATE_AVAILABLE)
        d = PluginUpdaterDialog(self.parent(),
                                initial_filter=FILTER_UPDATE_AVAILABLE)
        d.exec()
        if d.do_restart:
            QDialog.accept(self)
            from calibre.gui2.ui import get_gui
            gui = get_gui()
            if gui is not None:
                gui.quit(restart=True)

    def show_future(self, *args):
        config.set('new_version_notification', bool(self.cb.isChecked()))

    def accept(self):
        open_url(QUrl(get_download_url()))

        QDialog.accept(self)
Exemple #25
0
    def do_user_config(self, parent=None):
        '''
        This method shows a configuration dialog for this plugin. It returns
        True if the user clicks OK, False otherwise. The changes are
        automatically applied.
        '''
        from qt.core import QDialog, QDialogButtonBox, QVBoxLayout, \
                QLabel, Qt, QLineEdit
        from calibre.gui2 import gprefs

        prefname = 'plugin config dialog:' + self.type + ':' + self.name
        geom = gprefs.get(prefname, None)

        config_dialog = QDialog(parent)
        button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok
                                      | QDialogButtonBox.StandardButton.Cancel)
        v = QVBoxLayout(config_dialog)

        def size_dialog():
            if geom is None:
                config_dialog.resize(config_dialog.sizeHint())
            else:
                from qt.core import QApplication
                QApplication.instance().safe_restore_geometry(
                    config_dialog, geom)

        button_box.accepted.connect(config_dialog.accept)
        button_box.rejected.connect(config_dialog.reject)
        config_dialog.setWindowTitle(_('Customize') + ' ' + self.name)
        try:
            config_widget = self.config_widget()
        except NotImplementedError:
            config_widget = None

        if isinstance(config_widget, tuple):
            from calibre.gui2 import warning_dialog
            warning_dialog(parent,
                           _('Cannot configure'),
                           config_widget[0],
                           det_msg=config_widget[1],
                           show=True)
            return False

        if config_widget is not None:
            v.addWidget(config_widget)
            v.addWidget(button_box)
            size_dialog()
            config_dialog.exec()

            if config_dialog.result() == QDialog.DialogCode.Accepted:
                if hasattr(config_widget, 'validate'):
                    if config_widget.validate():
                        self.save_settings(config_widget)
                else:
                    self.save_settings(config_widget)
        else:
            from calibre.customize.ui import plugin_customization, \
                customize_plugin
            help_text = self.customization_help(gui=True)
            help_text = QLabel(help_text, config_dialog)
            help_text.setWordWrap(True)
            help_text.setTextInteractionFlags(
                Qt.TextInteractionFlag.LinksAccessibleByMouse
                | Qt.TextInteractionFlag.LinksAccessibleByKeyboard)
            help_text.setOpenExternalLinks(True)
            v.addWidget(help_text)
            sc = plugin_customization(self)
            if not sc:
                sc = ''
            sc = sc.strip()
            sc = QLineEdit(sc, config_dialog)
            v.addWidget(sc)
            v.addWidget(button_box)
            size_dialog()
            config_dialog.exec()

            if config_dialog.result() == QDialog.DialogCode.Accepted:
                sc = str(sc.text()).strip()
                customize_plugin(self, sc)

        geom = bytearray(config_dialog.saveGeometry())
        gprefs[prefname] = geom

        return config_dialog.result()
    def __init__(self,
                 settings,
                 all_formats,
                 supports_subdirs,
                 must_read_metadata,
                 supports_use_author_sort,
                 extra_customization_message,
                 device,
                 extra_customization_choices=None):

        QWidget.__init__(self)
        Ui_ConfigWidget.__init__(self)
        self.setupUi(self)

        self.settings = settings

        all_formats = set(all_formats)
        self.calibre_known_formats = device.FORMATS
        try:
            self.device_name = device.get_gui_name()
        except TypeError:
            self.device_name = getattr(device, 'gui_name', None) or _('Device')
        if device.USER_CAN_ADD_NEW_FORMATS:
            all_formats = all_formats | set(BOOK_EXTENSIONS)

        format_map = settings.format_map
        disabled_formats = all_formats.difference(format_map)
        for format in format_map + sorted(disabled_formats):
            item = QListWidgetItem(format, self.columns)
            item.setData(Qt.ItemDataRole.UserRole, (format))
            item.setFlags(Qt.ItemFlag.ItemIsEnabled
                          | Qt.ItemFlag.ItemIsUserCheckable
                          | Qt.ItemFlag.ItemIsSelectable)
            item.setCheckState(Qt.CheckState.Checked if format in
                               format_map else Qt.CheckState.Unchecked)

        self.column_up.clicked.connect(self.up_column)
        self.column_down.clicked.connect(self.down_column)

        if device.HIDE_FORMATS_CONFIG_BOX:
            self.groupBox.hide()

        if supports_subdirs:
            self.opt_use_subdirs.setChecked(self.settings.use_subdirs)
        else:
            self.opt_use_subdirs.hide()
        if not must_read_metadata:
            self.opt_read_metadata.setChecked(self.settings.read_metadata)
        else:
            self.opt_read_metadata.hide()
        if supports_use_author_sort:
            self.opt_use_author_sort.setChecked(self.settings.use_author_sort)
        else:
            self.opt_use_author_sort.hide()
        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(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(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(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(
                            settings.extra_customization[i])
                        self.opt_extra_customization[i].setCursorPosition(0)
                        self.extra_layout.addWidget(l, row_func(i, 0),
                                                    col_func(i))
                    self.extra_layout.addWidget(
                        self.opt_extra_customization[i], row_func(i, 1),
                        col_func(i))
            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 settings.extra_customization:
                    self.opt_extra_customization.setText(
                        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)
        self.opt_save_template.setText(settings.save_template)
Exemple #27
0
class RelaySetup(QDialog):
    def __init__(self, service, parent):
        QDialog.__init__(self, parent)

        self.l = l = QGridLayout()
        self.setLayout(l)
        self.bb = bb = QDialogButtonBox(
            QDialogButtonBox.StandardButton.Ok
            | QDialogButtonBox.StandardButton.Cancel)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        self.tl = QLabel(('<p>' + _(
            'Setup sending email using'
        ) + ' <b>{name}</b><p>' + _(
            'If you don\'t have an account, you can sign up for a free {name} email '
            'account at <a href="https://{url}">https://{url}</a>. {extra}')
                          ).format(**service))
        l.addWidget(self.tl, 0, 0, 3, 0)
        self.tl.setWordWrap(True)
        self.tl.setOpenExternalLinks(True)
        for name, label in (
            ['from_', _('Your %s &email address:')],
            ['username', _('Your %s &username:'******'password', _('Your %s &password:'******'name'])
            le = QLineEdit(self)
            setattr(self, name, le)
            setattr(self, name + '_label', la)
            r = l.rowCount()
            l.addWidget(la, r, 0)
            l.addWidget(le, r, 1)
            la.setBuddy(le)
            if name == 'password':
                self.ptoggle = QCheckBox(_('&Show password'), self)
                l.addWidget(self.ptoggle, r, 2)
                self.ptoggle.stateChanged.connect(
                    lambda s: self.password.setEchoMode(
                        QLineEdit.EchoMode.Normal if s == Qt.CheckState.Checked
                        else QLineEdit.EchoMode.Password))
        self.username.setText(service['username'])
        self.password.setEchoMode(QLineEdit.EchoMode.Password)
        self.bl = QLabel('<p>' + _(
            'If you plan to use email to send books to your Kindle, remember to'
            ' add your %s email address to the allowed email addresses in your '
            'Amazon.com Kindle management page.') % service['name'])
        self.bl.setWordWrap(True)
        l.addWidget(self.bl, l.rowCount(), 0, 3, 0)
        l.addWidget(bb, l.rowCount(), 0, 3, 0)
        self.setWindowTitle(_('Setup') + ' ' + service['name'])
        self.resize(self.sizeHint())
        self.service = service

    def accept(self):
        un = unicode_type(self.username.text())
        if self.service.get('at_in_username', False) and '@' not in un:
            return error_dialog(
                self,
                _('Incorrect username'),
                _('%s needs the full email address as your username') %
                self.service['name'],
                show=True)
        QDialog.accept(self)
Exemple #28
0
class UnpackBook(QDialog):

    def __init__(self, parent, book_id, fmts, db):
        QDialog.__init__(self, parent)
        self.setWindowIcon(QIcon(I('unpack-book.png')))
        self.book_id, self.fmts, self.db_ref = book_id, fmts, weakref.ref(db)
        self._exploded = None
        self._cleanup_dirs = []
        self._cleanup_files = []

        self.setup_ui()
        self.setWindowTitle(_('Unpack book') + ' - ' + db.title(book_id,
            index_is_id=True))

        button = self.fmt_choice_buttons[0]
        button_map = {str(x.text()):x for x in self.fmt_choice_buttons}
        of = prefs['output_format'].upper()
        df = tweaks.get('default_tweak_format', None)
        lf = gprefs.get('last_tweak_format', None)
        if df and df.lower() == 'remember' and lf in button_map:
            button = button_map[lf]
        elif df and df.upper() in button_map:
            button = button_map[df.upper()]
        elif of in button_map:
            button = button_map[of]
        button.setChecked(True)

        self.init_state()
        for button in self.fmt_choice_buttons:
            button.toggled.connect(self.init_state)

    def init_state(self, *args):
        self._exploded = None
        self.preview_button.setEnabled(False)
        self.rebuild_button.setEnabled(False)
        self.explode_button.setEnabled(True)

    def setup_ui(self):  # {{{
        self._g = g = QHBoxLayout(self)
        self.setLayout(g)
        self._l = l = QVBoxLayout()
        g.addLayout(l)

        fmts = sorted(x.upper() for x in self.fmts)
        self.fmt_choice_box = QGroupBox(_('Choose the format to unpack:'), self)
        self._fl = fl = QHBoxLayout()
        self.fmt_choice_box.setLayout(self._fl)
        self.fmt_choice_buttons = [QRadioButton(y, self) for y in fmts]
        for x in self.fmt_choice_buttons:
            fl.addWidget(x, stretch=10 if x is self.fmt_choice_buttons[-1] else
                    0)
        l.addWidget(self.fmt_choice_box)
        self.fmt_choice_box.setVisible(len(fmts) > 1)

        self.help_label = QLabel(_('''\
            <h2>About Unpack book</h2>
            <p>Unpack book allows you to fine tune the appearance of an e-book by
            making small changes to its internals. In order to use Unpack book,
            you need to know a little bit about HTML and CSS, technologies that
            are used in e-books. Follow the steps:</p>
            <br>
            <ol>
            <li>Click "Explode book": This will "explode" the book into its
            individual internal components.<br></li>
            <li>Right click on any individual file and select "Open with..." to
            edit it in your favorite text editor.<br></li>
            <li>When you are done: <b>close the file browser window
            and the editor windows you used to make your tweaks</b>. Then click
            the "Rebuild book" button, to update the book in your calibre
            library.</li>
            </ol>'''))
        self.help_label.setWordWrap(True)
        self._fr = QFrame()
        self._fr.setFrameShape(QFrame.Shape.VLine)
        g.addWidget(self._fr)
        g.addWidget(self.help_label)

        self._b = b = QGridLayout()
        left, top, right, bottom = b.getContentsMargins()
        top += top
        b.setContentsMargins(left, top, right, bottom)
        l.addLayout(b, stretch=10)

        self.explode_button = QPushButton(QIcon(I('wizard.png')), _('&Explode book'))
        self.preview_button = QPushButton(QIcon(I('view.png')), _('&Preview book'))
        self.cancel_button  = QPushButton(QIcon(I('window-close.png')), _('&Cancel'))
        self.rebuild_button = QPushButton(QIcon(I('exec.png')), _('&Rebuild book'))

        self.explode_button.setToolTip(
                _('Explode the book to edit its components'))
        self.preview_button.setToolTip(
                _('Preview the result of your changes'))
        self.cancel_button.setToolTip(
                _('Abort without saving any changes'))
        self.rebuild_button.setToolTip(
            _('Save your changes and update the book in the calibre library'))

        a = b.addWidget
        a(self.explode_button, 0, 0, 1, 1)
        a(self.preview_button, 0, 1, 1, 1)
        a(self.cancel_button,  1, 0, 1, 1)
        a(self.rebuild_button, 1, 1, 1, 1)

        for x in ('explode', 'preview', 'cancel', 'rebuild'):
            getattr(self, x+'_button').clicked.connect(getattr(self, x))

        self.msg = QLabel('dummy', self)
        self.msg.setVisible(False)
        self.msg.setStyleSheet('''
        QLabel {
            text-align: center;
            background-color: white;
            color: black;
            border-width: 1px;
            border-style: solid;
            border-radius: 20px;
            font-size: x-large;
            font-weight: bold;
        }
        ''')

        self.resize(self.sizeHint() + QSize(40, 10))
    # }}}

    def show_msg(self, msg):
        self.msg.setText(msg)
        self.msg.resize(self.size() - QSize(50, 25))
        self.msg.move((self.width() - self.msg.width())//2,
                (self.height() - self.msg.height())//2)
        self.msg.setVisible(True)

    def hide_msg(self):
        self.msg.setVisible(False)

    def explode(self):
        self.show_msg(_('Exploding, please wait...'))
        if len(self.fmt_choice_buttons) > 1:
            gprefs.set('last_tweak_format', self.current_format.upper())
        QTimer.singleShot(5, self.do_explode)

    def ask_question(self, msg):
        return question_dialog(self, _('Are you sure?'), msg)

    def do_explode(self):
        from calibre.ebooks.tweak import get_tools, Error, WorkerError
        tdir = PersistentTemporaryDirectory('_tweak_explode')
        self._cleanup_dirs.append(tdir)
        det_msg = None
        try:
            src = self.db.format(self.book_id, self.current_format,
                    index_is_id=True, as_path=True)
            self._cleanup_files.append(src)
            exploder = get_tools(self.current_format)[0]
            opf = exploder(src, tdir, question=self.ask_question)
        except WorkerError as e:
            det_msg = e.orig_tb
        except Error as e:
            return error_dialog(self, _('Failed to unpack'),
                (_('Could not explode the %s file.')%self.current_format) + ' ' + as_unicode(e), show=True)
        except:
            import traceback
            det_msg = traceback.format_exc()
        finally:
            self.hide_msg()

        if det_msg is not None:
            return error_dialog(self, _('Failed to unpack'),
                _('Could not explode the %s file. Click "Show details" for '
                    'more information.')%self.current_format, det_msg=det_msg,
                show=True)

        if opf is None:
            # The question was answered with No
            return

        self._exploded = tdir
        self.explode_button.setEnabled(False)
        self.preview_button.setEnabled(True)
        self.rebuild_button.setEnabled(True)
        open_local_file(tdir)

    def rebuild_it(self):
        from calibre.ebooks.tweak import get_tools, WorkerError
        src_dir = self._exploded
        det_msg = None
        of = PersistentTemporaryFile('_tweak_rebuild.'+self.current_format.lower())
        of.close()
        of = of.name
        self._cleanup_files.append(of)
        try:
            rebuilder = get_tools(self.current_format)[1]
            rebuilder(src_dir, of)
        except WorkerError as e:
            det_msg = e.orig_tb
        except:
            import traceback
            det_msg = traceback.format_exc()
        finally:
            self.hide_msg()

        if det_msg is not None:
            error_dialog(self, _('Failed to rebuild file'),
                    _('Failed to rebuild %s. For more information, click '
                        '"Show details".')%self.current_format,
                    det_msg=det_msg, show=True)
            return None

        return of

    def preview(self):
        self.show_msg(_('Rebuilding, please wait...'))
        QTimer.singleShot(5, self.do_preview)

    def do_preview(self):
        rebuilt = self.rebuild_it()
        if rebuilt is not None:
            self.parent().iactions['View']._view_file(rebuilt)

    def rebuild(self):
        self.show_msg(_('Rebuilding, please wait...'))
        QTimer.singleShot(5, self.do_rebuild)

    def do_rebuild(self):
        rebuilt = self.rebuild_it()
        if rebuilt is not None:
            fmt = os.path.splitext(rebuilt)[1][1:].upper()
            with open(rebuilt, 'rb') as f:
                self.db.add_format(self.book_id, fmt, f, index_is_id=True)
            self.accept()

    def cancel(self):
        self.reject()

    def cleanup(self):
        if ismacos and self._exploded:
            try:
                import appscript
                self.finder = appscript.app('Finder')
                self.finder.Finder_windows[os.path.basename(self._exploded)].close()
            except:
                pass

        for f in self._cleanup_files:
            try:
                os.remove(f)
            except:
                pass

        for d in self._cleanup_dirs:
            try:
                shutil.rmtree(d)
            except:
                pass

    @property
    def db(self):
        return self.db_ref()

    @property
    def current_format(self):
        for b in self.fmt_choice_buttons:
            if b.isChecked():
                return str(b.text())
Exemple #29
0
class ItemView(QStackedWidget):  # {{{

    add_new_item = pyqtSignal(object, object)
    delete_item = pyqtSignal()
    flatten_item = pyqtSignal()
    go_to_root = pyqtSignal()
    create_from_xpath = pyqtSignal(object, object)
    create_from_links = pyqtSignal()
    create_from_files = pyqtSignal()
    flatten_toc = pyqtSignal()

    def __init__(self, parent, prefs):
        QStackedWidget.__init__(self, parent)
        self.prefs = prefs
        self.setMinimumWidth(250)
        self.root_pane = rp = QWidget(self)
        self.item_pane = ip = QWidget(self)
        self.current_item = None
        sa = QScrollArea(self)
        sa.setWidgetResizable(True)
        sa.setWidget(rp)
        self.addWidget(sa)
        sa = QScrollArea(self)
        sa.setWidgetResizable(True)
        sa.setWidget(ip)
        self.addWidget(sa)

        self.l1 = la = QLabel('<p>' + _(
            'You can edit existing entries in the Table of Contents by clicking them'
            ' in the panel to the left.'
        ) + '<p>' + _(
            'Entries with a green tick next to them point to a location that has '
            'been verified to exist. Entries with a red dot are broken and may need'
            ' to be fixed.'))
        la.setStyleSheet('QLabel { margin-bottom: 20px }')
        la.setWordWrap(True)
        l = rp.l = QVBoxLayout()
        rp.setLayout(l)
        l.addWidget(la)
        self.add_new_to_root_button = b = QPushButton(_('Create a &new entry'))
        b.clicked.connect(self.add_new_to_root)
        l.addWidget(b)
        l.addStretch()

        self.cfmhb = b = QPushButton(_('Generate ToC from &major headings'))
        b.clicked.connect(self.create_from_major_headings)
        b.setToolTip(
            textwrap.fill(
                _('Generate a Table of Contents from the major headings in the book.'
                  ' This will work if the book identifies its headings using HTML'
                  ' heading tags. Uses the <h1>, <h2> and <h3> tags.')))
        l.addWidget(b)
        self.cfmab = b = QPushButton(_('Generate ToC from &all headings'))
        b.clicked.connect(self.create_from_all_headings)
        b.setToolTip(
            textwrap.fill(
                _('Generate a Table of Contents from all the headings in the book.'
                  ' This will work if the book identifies its headings using HTML'
                  ' heading tags. Uses the <h1-6> tags.')))
        l.addWidget(b)

        self.lb = b = QPushButton(_('Generate ToC from &links'))
        b.clicked.connect(self.create_from_links)
        b.setToolTip(
            textwrap.fill(
                _('Generate a Table of Contents from all the links in the book.'
                  ' Links that point to destinations that do not exist in the book are'
                  ' ignored. Also multiple links with the same destination or the same'
                  ' text are ignored.')))
        l.addWidget(b)

        self.cfb = b = QPushButton(_('Generate ToC from &files'))
        b.clicked.connect(self.create_from_files)
        b.setToolTip(
            textwrap.fill(
                _('Generate a Table of Contents from individual files in the book.'
                  ' Each entry in the ToC will point to the start of the file, the'
                  ' text of the entry will be the "first line" of text from the file.'
                  )))
        l.addWidget(b)

        self.xpb = b = QPushButton(_('Generate ToC from &XPath'))
        b.clicked.connect(self.create_from_user_xpath)
        b.setToolTip(
            textwrap.fill(
                _('Generate a Table of Contents from arbitrary XPath expressions.'
                  )))
        l.addWidget(b)

        self.fal = b = QPushButton(_('&Flatten the ToC'))
        b.clicked.connect(self.flatten_toc)
        b.setToolTip(
            textwrap.fill(
                _('Flatten the Table of Contents, putting all entries at the top level'
                  )))
        l.addWidget(b)

        l.addStretch()
        self.w1 = la = QLabel(
            _('<b>WARNING:</b> calibre only supports the '
              'creation of linear ToCs in AZW3 files. In a '
              'linear ToC every entry must point to a '
              'location after the previous entry. If you '
              'create a non-linear ToC it will be '
              'automatically re-arranged inside the AZW3 file.'))
        la.setWordWrap(True)
        l.addWidget(la)

        l = ip.l = QGridLayout()
        ip.setLayout(l)
        la = ip.heading = QLabel('')
        l.addWidget(la, 0, 0, 1, 2)
        la.setWordWrap(True)
        la = ip.la = QLabel(
            _('You can move this entry around the Table of Contents by drag '
              'and drop or using the up and down buttons to the left'))
        la.setWordWrap(True)
        l.addWidget(la, 1, 0, 1, 2)

        # Item status
        ip.hl1 = hl = QFrame()
        hl.setFrameShape(QFrame.Shape.HLine)
        l.addWidget(hl, l.rowCount(), 0, 1, 2)
        self.icon_label = QLabel()
        self.status_label = QLabel()
        self.status_label.setWordWrap(True)
        l.addWidget(self.icon_label, l.rowCount(), 0)
        l.addWidget(self.status_label, l.rowCount() - 1, 1)
        ip.hl2 = hl = QFrame()
        hl.setFrameShape(QFrame.Shape.HLine)
        l.addWidget(hl, l.rowCount(), 0, 1, 2)

        # Edit/remove item
        rs = l.rowCount()
        ip.b1 = b = QPushButton(QIcon(I('edit_input.png')),
                                _('Change the &location this entry points to'),
                                self)
        b.clicked.connect(self.edit_item)
        l.addWidget(b, l.rowCount() + 1, 0, 1, 2)
        ip.b2 = b = QPushButton(QIcon(I('trash.png')), _('&Remove this entry'),
                                self)
        l.addWidget(b, l.rowCount(), 0, 1, 2)
        b.clicked.connect(self.delete_item)
        ip.hl3 = hl = QFrame()
        hl.setFrameShape(QFrame.Shape.HLine)
        l.addWidget(hl, l.rowCount(), 0, 1, 2)
        l.setRowMinimumHeight(rs, 20)

        # Add new item
        rs = l.rowCount()
        ip.b3 = b = QPushButton(QIcon(I('plus.png')),
                                _('New entry &inside this entry'))
        connect_lambda(b.clicked, self, lambda self: self.add_new('inside'))
        l.addWidget(b, l.rowCount() + 1, 0, 1, 2)
        ip.b4 = b = QPushButton(QIcon(I('plus.png')),
                                _('New entry &above this entry'))
        connect_lambda(b.clicked, self, lambda self: self.add_new('before'))
        l.addWidget(b, l.rowCount(), 0, 1, 2)
        ip.b5 = b = QPushButton(QIcon(I('plus.png')),
                                _('New entry &below this entry'))
        connect_lambda(b.clicked, self, lambda self: self.add_new('after'))
        l.addWidget(b, l.rowCount(), 0, 1, 2)
        # Flatten entry
        ip.b3 = b = QPushButton(QIcon(I('heuristics.png')),
                                _('&Flatten this entry'))
        b.clicked.connect(self.flatten_item)
        b.setToolTip(
            _('All children of this entry are brought to the same '
              'level as this entry.'))
        l.addWidget(b, l.rowCount() + 1, 0, 1, 2)

        ip.hl4 = hl = QFrame()
        hl.setFrameShape(QFrame.Shape.HLine)
        l.addWidget(hl, l.rowCount(), 0, 1, 2)
        l.setRowMinimumHeight(rs, 20)

        # Return to welcome
        rs = l.rowCount()
        ip.b4 = b = QPushButton(QIcon(I('back.png')),
                                _('&Return to welcome screen'))
        b.clicked.connect(self.go_to_root)
        b.setToolTip(_('Go back to the top level view'))
        l.addWidget(b, l.rowCount() + 1, 0, 1, 2)

        l.setRowMinimumHeight(rs, 20)

        l.addWidget(QLabel(), l.rowCount(), 0, 1, 2)
        l.setColumnStretch(1, 10)
        l.setRowStretch(l.rowCount() - 1, 10)
        self.w2 = la = QLabel(self.w1.text())
        self.w2.setWordWrap(True)
        l.addWidget(la, l.rowCount(), 0, 1, 2)

    def ask_if_duplicates_should_be_removed(self):
        return not question_dialog(
            self,
            _('Remove duplicates'),
            _('Should headings with the same text at the same level be included?'
              ),
            yes_text=_('&Include duplicates'),
            no_text=_('&Remove duplicates'))

    def create_from_major_headings(self):
        self.create_from_xpath.emit(['//h:h%d' % i for i in range(1, 4)],
                                    self.ask_if_duplicates_should_be_removed())

    def create_from_all_headings(self):
        self.create_from_xpath.emit(['//h:h%d' % i for i in range(1, 7)],
                                    self.ask_if_duplicates_should_be_removed())

    def create_from_user_xpath(self):
        d = XPathDialog(self, self.prefs)
        if d.exec_() == QDialog.DialogCode.Accepted and d.xpaths:
            self.create_from_xpath.emit(d.xpaths,
                                        d.remove_duplicates_cb.isChecked())

    def hide_azw3_warning(self):
        self.w1.setVisible(False), self.w2.setVisible(False)

    def add_new_to_root(self):
        self.add_new_item.emit(None, None)

    def add_new(self, where):
        self.add_new_item.emit(self.current_item, where)

    def edit_item(self):
        self.add_new_item.emit(self.current_item, None)

    def __call__(self, item):
        if item is None:
            self.current_item = None
            self.setCurrentIndex(0)
        else:
            self.current_item = item
            self.setCurrentIndex(1)
            self.populate_item_pane()

    def populate_item_pane(self):
        item = self.current_item
        name = unicode_type(item.data(0, Qt.ItemDataRole.DisplayRole) or '')
        self.item_pane.heading.setText('<h2>%s</h2>' % name)
        self.icon_label.setPixmap(
            item.data(0, Qt.ItemDataRole.DecorationRole).pixmap(32, 32))
        tt = _('This entry points to an existing destination')
        toc = item.data(0, Qt.ItemDataRole.UserRole)
        if toc.dest_exists is False:
            tt = _('The location this entry points to does not exist')
        elif toc.dest_exists is None:
            tt = ''
        self.status_label.setText(tt)

    def data_changed(self, item):
        if item is self.current_item:
            self.populate_item_pane()
Exemple #30
0
class DownloadDialog(QDialog):  # {{{

    def __init__(self, url, fname, parent):
        QDialog.__init__(self, parent)
        self.setWindowTitle(_('Download %s')%fname)
        self.l = QVBoxLayout(self)
        self.purl = urlparse(url)
        self.msg = QLabel(_('Downloading <b>%(fname)s</b> from %(url)s')%dict(
            fname=fname, url=self.purl.netloc))
        self.msg.setWordWrap(True)
        self.l.addWidget(self.msg)
        self.pb = QProgressBar(self)
        self.pb.setMinimum(0)
        self.pb.setMaximum(0)
        self.l.addWidget(self.pb)
        self.bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Cancel, Qt.Orientation.Horizontal, self)
        self.l.addWidget(self.bb)
        self.bb.rejected.connect(self.reject)
        sz = self.sizeHint()
        self.resize(max(sz.width(), 400), sz.height())

        fpath = PersistentTemporaryFile(os.path.splitext(fname)[1])
        fpath.close()
        self.fpath = fpath.name

        self.worker = Worker(url, self.fpath, Queue())
        self.rejected = False

    def reject(self):
        self.rejected = True
        QDialog.reject(self)

    def start_download(self):
        self.worker.start()
        QTimer.singleShot(50, self.update)
        self.exec_()
        if self.worker.err is not None:
            error_dialog(self.parent(), _('Download failed'),
                _('Failed to download from %(url)r with error: %(err)s')%dict(
                    url=self.worker.url, err=self.worker.err),
                det_msg=self.worker.tb, show=True)

    def update(self):
        if self.rejected:
            return

        try:
            progress = self.worker.rq.get_nowait()
        except Empty:
            pass
        else:
            self.update_pb(progress)

        if not self.worker.is_alive():
            return self.accept()
        QTimer.singleShot(50, self.update)

    def update_pb(self, progress):
        transferred, block_size, total = progress
        if total == -1:
            self.pb.setMaximum(0)
            self.pb.setMinimum(0)
            self.pb.setValue(0)
        else:
            so_far = transferred * block_size
            self.pb.setMaximum(max(total, so_far))
            self.pb.setValue(so_far)

    @property
    def err(self):
        return self.worker.err