Exemplo n.º 1
0
def show_config_widget(category, name, gui=None, show_restart_msg=False,
        parent=None, never_shutdown=False):
    '''
    Show the preferences plugin identified by category and name

    :param gui: gui instance, if None a hidden gui is created
    :param show_restart_msg: If True and the preferences plugin indicates a
    restart is required, show a message box telling the user to restart
    :param parent: The parent of the displayed dialog

    :return: True iff a restart is required for the changes made by the user to
    take effect
    '''
    from calibre.gui2 import gprefs
    pl = get_plugin(category, name)
    d = ConfigDialog(parent)
    d.resize(750, 550)
    conf_name = 'config_widget_dialog_geometry_%s_%s'%(category, name)
    geom = gprefs.get(conf_name, None)
    d.setWindowTitle(_('Configure ') + name)
    d.setWindowIcon(QIcon(I('config.png')))
    bb = QDialogButtonBox(d)
    bb.setStandardButtons(bb.Apply|bb.Cancel|bb.RestoreDefaults)
    bb.accepted.connect(d.accept)
    bb.rejected.connect(d.reject)
    w = pl.create_widget(d)
    d.set_widget(w)
    bb.button(bb.RestoreDefaults).clicked.connect(w.restore_defaults)
    bb.button(bb.RestoreDefaults).setEnabled(w.supports_restoring_to_defaults)
    bb.button(bb.Apply).setEnabled(False)
    bb.button(bb.Apply).clicked.connect(d.accept)
    def onchange():
        b = bb.button(bb.Apply)
        b.setEnabled(True)
        b.setDefault(True)
        b.setAutoDefault(True)
    w.changed_signal.connect(onchange)
    bb.button(bb.Cancel).setFocus(True)
    l = QVBoxLayout()
    d.setLayout(l)
    l.addWidget(w)
    l.addWidget(bb)
    mygui = gui is None
    if gui is None:
        gui = init_gui()
        mygui = True
    w.genesis(gui)
    w.initialize()
    if geom is not None:
        d.restoreGeometry(geom)
    d.exec_()
    geom = bytearray(d.saveGeometry())
    gprefs[conf_name] = geom
    rr = getattr(d, 'restart_required', False)
    if show_restart_msg and rr:
        from calibre.gui2 import warning_dialog
        warning_dialog(gui, 'Restart required', 'Restart required', show=True)
    if mygui and not never_shutdown:
        gui.shutdown()
    return rr
Exemplo n.º 2
0
class Wizard(QDialog):

    def __init__(self, parent=None):
        QDialog.__init__(self, parent)
        self.resize(440, 480)
        self.verticalLayout = QVBoxLayout(self)
        self.widget = WizardWidget(self)
        self.verticalLayout.addWidget(self.widget)
        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setOrientation(Qt.Horizontal)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
        self.verticalLayout.addWidget(self.buttonBox)

        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)
        self.setModal(Qt.WindowModal)

    @property
    def xpath(self):
        return self.widget.xpath
class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName(_fromUtf8("Dialog"))
        Dialog.resize(497, 235)
        self.gridLayout = QGridLayout(Dialog)
        self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
        self.icon_label = QLabel(Dialog)
        self.icon_label.setMaximumSize(QtCore.QSize(COVER_ICON_SIZE, COVER_ICON_SIZE))
        self.icon_label.setText(_fromUtf8(""))
        self.icon_label.setPixmap(QPixmap(_fromUtf8(I("dialog_warning.png"))))
        self.icon_label.setScaledContents(False)
        self.icon_label.setObjectName(_fromUtf8("icon_label"))
        self.gridLayout.addWidget(self.icon_label, 0, 0, 1, 1)
        self.msg = QLabel(Dialog)
        self.msg.setText(_fromUtf8(""))
        self.msg.setWordWrap(True)
        self.msg.setOpenExternalLinks(True)
        self.msg.setObjectName(_fromUtf8("msg"))
        self.gridLayout.addWidget(self.msg, 0, 1, 1, 1)
        self.det_msg = QPlainTextEdit(Dialog)
        self.det_msg.setReadOnly(True)
        self.det_msg.setObjectName(_fromUtf8("det_msg"))
        self.gridLayout.addWidget(self.det_msg, 1, 0, 1, 2)
        self.bb = QDialogButtonBox(Dialog)
        self.bb.setOrientation(QtCore.Qt.Horizontal)
        self.bb.setStandardButtons(QDialogButtonBox.Ok)
        self.bb.setObjectName(_fromUtf8("bb"))
        self.gridLayout.addWidget(self.bb, 3, 0, 1, 2)
        self.toggle_checkbox = QCheckBox(Dialog)
        self.toggle_checkbox.setText(_fromUtf8(""))
        self.toggle_checkbox.setObjectName(_fromUtf8("toggle_checkbox"))
        self.gridLayout.addWidget(self.toggle_checkbox, 2, 0, 1, 2)

        self.retranslateUi(Dialog)
        self.bb.accepted.connect(Dialog.accept)
        self.bb.rejected.connect(Dialog.reject)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        Dialog.setWindowTitle(_("Dialog"))
Exemplo n.º 4
0
class ProceedQuestion(QWidget):

    ask_question = pyqtSignal(object, object, object)

    @pyqtProperty(float)
    def show_fraction(self):
        return self._show_fraction

    @show_fraction.setter
    def show_fraction(self, val):
        self._show_fraction = max(0, min(1, float(val)))
        self.update()

    def __init__(self, parent):
        QWidget.__init__(self, parent)
        self.setVisible(False)
        parent.installEventFilter(self)

        self._show_fraction = 0.0
        self.show_animation = a = QPropertyAnimation(self, b"show_fraction", self)
        a.setDuration(1000), a.setEasingCurve(QEasingCurve.OutQuad)
        a.setStartValue(0.0), a.setEndValue(1.0)
        a.finished.connect(self.stop_show_animation)
        self.rendered_pixmap = None

        self.questions = []

        self.icon = ic = Icon(self)
        self.msg_label = msg = QLabel('some random filler text')
        msg.setWordWrap(True)
        self.bb = QDialogButtonBox()
        self.bb.accepted.connect(self.accept)
        self.bb.rejected.connect(self.reject)
        self.log_button = self.bb.addButton(_('View log'), self.bb.ActionRole)
        self.log_button.setIcon(QIcon(I('debug.png')))
        self.log_button.clicked.connect(self.show_log)
        self.copy_button = self.bb.addButton(_('&Copy to clipboard'),
                self.bb.ActionRole)
        self.copy_button.clicked.connect(self.copy_to_clipboard)
        self.action_button = self.bb.addButton('', self.bb.ActionRole)
        self.action_button.clicked.connect(self.action_clicked)
        self.show_det_msg = _('Show &details')
        self.hide_det_msg = _('Hide &details')
        self.det_msg_toggle = self.bb.addButton(self.show_det_msg, self.bb.ActionRole)
        self.det_msg_toggle.clicked.connect(self.toggle_det_msg)
        self.det_msg_toggle.setToolTip(
                _('Show detailed information about this error'))
        self.det_msg = PlainTextEdit(self)
        self.det_msg.setReadOnly(True)
        self.bb.setStandardButtons(self.bb.Yes|self.bb.No|self.bb.Ok)
        self.bb.button(self.bb.Yes).setDefault(True)
        self.title_label = title = QLabel('A dummy title')
        f = title.font()
        f.setBold(True)
        title.setFont(f)

        self.checkbox = QCheckBox('', self)

        self._l = l = QVBoxLayout(self)
        self._h = h = QHBoxLayout()
        self._v = v = QVBoxLayout()
        v.addWidget(title), v.addWidget(msg)
        h.addWidget(ic), h.addSpacing(10), h.addLayout(v), l.addLayout(h)
        l.addSpacing(5)
        l.addWidget(self.checkbox)
        l.addWidget(self.det_msg)
        l.addWidget(self.bb)

        self.ask_question.connect(self.do_ask_question,
                type=Qt.QueuedConnection)
        self.setFocusPolicy(Qt.NoFocus)
        for child in self.findChildren(QWidget):
            child.setFocusPolicy(Qt.NoFocus)
        self.setFocusProxy(self.parent())
        self.resize_timer = t = QTimer(self)
        t.setSingleShot(True), t.setInterval(100), t.timeout.connect(self.parent_resized)

    def eventFilter(self, obj, ev):
        if ev.type() == ev.Resize and self.isVisible():
            self.resize_timer.start()
        return False

    def parent_resized(self):
        if self.isVisible():
            self.do_resize()

    def copy_to_clipboard(self, *args):
        QApplication.clipboard().setText(
                'calibre, version %s\n%s: %s\n\n%s' %
                (__version__, unicode_type(self.windowTitle()),
                    unicode_type(self.msg_label.text()),
                    unicode_type(self.det_msg.toPlainText())))
        self.copy_button.setText(_('Copied'))

    def action_clicked(self):
        if self.questions:
            q = self.questions[0]
            self.questions[0] = q._replace(callback=q.action_callback)
        self.accept()

    def accept(self):
        if self.questions:
            payload, callback, cancel_callback = self.questions[0][:3]
            self.questions = self.questions[1:]
            cb = None
            if self.checkbox.isVisible():
                cb = bool(self.checkbox.isChecked())
            self.ask_question.emit(callback, payload, cb)
        self.hide()

    def reject(self):
        if self.questions:
            payload, callback, cancel_callback = self.questions[0][:3]
            self.questions = self.questions[1:]
            cb = None
            if self.checkbox.isVisible():
                cb = bool(self.checkbox.isChecked())
            self.ask_question.emit(cancel_callback, payload, cb)
        self.hide()

    def do_ask_question(self, callback, payload, checkbox_checked):
        if callable(callback):
            args = [payload]
            if checkbox_checked is not None:
                args.append(checkbox_checked)
            callback(*args)
        self.show_question()

    def toggle_det_msg(self, *args):
        vis = unicode_type(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):
        sz = self.sizeHint()
        sz.setWidth(min(self.parent().width(), sz.width()))
        sb = self.parent().statusBar().height() + 10
        sz.setHeight(min(self.parent().height() - sb, sz.height()))
        self.resize(sz)
        self.position_widget()

    def show_question(self):
        if not self.questions:
            return
        if not self.isVisible():
            question = self.questions[0]
            self.msg_label.setText(question.msg)
            self.icon.set_icon(question.icon)
            self.title_label.setText(question.title)
            self.log_button.setVisible(bool(question.html_log))
            self.copy_button.setText(_('&Copy to clipboard'))
            if question.action_callback is not None:
                self.action_button.setText(question.action_label or '')
                self.action_button.setIcon(
                    QIcon() if question.action_icon is None else question.action_icon)
            # Force the button box to relayout its buttons, as button text
            # might have changed
            self.bb.setOrientation(Qt.Vertical), self.bb.setOrientation(Qt.Horizontal)
            self.det_msg.setPlainText(question.det_msg or '')
            self.det_msg.setVisible(False)
            self.det_msg_toggle.setVisible(bool(question.det_msg))
            self.det_msg_toggle.setText(self.show_det_msg)
            self.checkbox.setVisible(question.checkbox_msg is not None)
            if question.checkbox_msg is not None:
                self.checkbox.setText(question.checkbox_msg)
                self.checkbox.setChecked(question.checkbox_checked)
            self.bb.button(self.bb.Ok).setVisible(question.show_ok)
            self.bb.button(self.bb.Yes).setVisible(not question.show_ok)
            self.bb.button(self.bb.No).setVisible(not question.show_ok)
            self.copy_button.setVisible(bool(question.show_copy_button))
            self.action_button.setVisible(question.action_callback is not None)
            self.toggle_det_msg() if question.show_det else self.do_resize()
            self.show_widget()
            button = self.action_button if question.focus_action and question.action_callback is not None else \
                (self.bb.button(self.bb.Ok) if question.show_ok else self.bb.button(self.bb.Yes))
            button.setDefault(True)
            self.raise_()
            self.start_show_animation()

    def start_show_animation(self):
        if self.rendered_pixmap is not None:
            return

        dpr = getattr(self, 'devicePixelRatioF', self.devicePixelRatio)()
        p = QImage(dpr * self.size(), QImage.Format_ARGB32_Premultiplied)
        p.setDevicePixelRatio(dpr)
        self.render(p)
        self.rendered_pixmap = QPixmap.fromImage(p)
        self.original_visibility = v = []
        for child in self.findChildren(QWidget):
            if child.isVisible():
                child.setVisible(False)
                v.append(child)
        self.show_animation.start()

    def stop_show_animation(self):
        self.rendered_pixmap = None
        [c.setVisible(True) for c in getattr(self, 'original_visibility', ())]
        self.update()
        for child in self.findChildren(QWidget):
            child.update()
            if hasattr(child, 'viewport'):
                child.viewport().update()

    def position_widget(self):
        geom = self.parent().geometry()
        x = geom.width() - self.width() - 5
        sb = self.parent().statusBar()
        if sb is None:
            y = geom.height() - self.height()
        else:
            y = sb.geometry().top() - self.height()
        self.move(x, y)

    def show_widget(self):
        self.show()
        self.position_widget()

    def dummy_question(self, action_label=None):
        self(lambda *args:args, (), 'dummy log', 'Log Viewer', 'A Dummy Popup',
             'This is a dummy popup to easily test things, with a long line of text that should wrap. '
             'This is a dummy popup to easily test things, with a long line of text that should wrap',
             checkbox_msg='A dummy checkbox',
             action_callback=lambda *args: args, action_label=action_label or 'An action')

    def __call__(self, callback, payload, html_log, log_viewer_title, title,
            msg, det_msg='', show_copy_button=False, cancel_callback=None,
            log_is_file=False, checkbox_msg=None, checkbox_checked=False,
            action_callback=None, action_label=None, action_icon=None, focus_action=False,
            show_det=False, show_ok=False, icon=None, log_viewer_unique_name=None, **kw):
        '''
        A non modal popup that notifies the user that a background task has
        been completed. This class guarantees that only a single popup is
        visible at any one time. Other requests are queued and displayed after
        the user dismisses the current popup.

        :param callback: A callable that is called with payload if the user
        asks to proceed. Note that this is always called in the GUI thread.
        :param cancel_callback: A callable that is called with the payload if
        the users asks not to proceed.
        :param payload: Arbitrary object, passed to callback
        :param html_log: An HTML or plain text log
        :param log_viewer_title: The title for the log viewer window
        :param title: The title for this popup
        :param msg: The msg to display
        :param det_msg: Detailed message
        :param log_is_file: If True the html_log parameter is interpreted as
                            the path to a file on disk containing the log
                            encoded with utf-8
        :param checkbox_msg: If not None, a checkbox is displayed in the
                             dialog, showing this message. The callback is
                             called with both the payload and the state of the
                             checkbox as arguments.
        :param checkbox_checked: If True the checkbox is checked by default.
        :param action_callback: If not None, an extra button is added, which
                                when clicked will cause action_callback to be called
                                instead of callback. action_callback is called in
                                exactly the same way as callback.
        :param action_label: The text on the action button
        :param action_icon: The icon for the action button, must be a QIcon object or None
        :param focus_action: If True, the action button will be focused instead of the Yes button
        :param show_det: If True, the Detailed message will be shown initially
        :param show_ok: If True, OK will be shown instead of YES/NO
        :param icon: The icon to be used for this popop (defaults to question mark). Can be either a QIcon or a string to be used with I()
        :log_viewer_unique_name: If set, ViewLog will remember/reuse its size for this name in calibre.gui2.gprefs
        '''
        question = Question(
            payload, callback, cancel_callback, title, msg, html_log,
            log_viewer_title, log_is_file, det_msg, show_copy_button,
            checkbox_msg, checkbox_checked, action_callback, action_label,
            action_icon, focus_action, show_det, show_ok, icon, log_viewer_unique_name)
        self.questions.append(question)
        self.show_question()

    def show_log(self):
        if self.questions:
            q = self.questions[0]
            log = q.html_log
            if q.log_is_file:
                with open(log, 'rb') as f:
                    log = f.read().decode('utf-8')
            self.log_viewer = ViewLog(q.log_viewer_title, log,
                        parent=self, unique_name=q.log_viewer_unique_name)

    def paintEvent(self, ev):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing, True)
        painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
        try:
            if self.rendered_pixmap is None:
                self.paint_background(painter)
            else:
                self.animated_paint(painter)
        finally:
            painter.end()

    def animated_paint(self, painter):
        top = (1 - self._show_fraction) * self.height()
        painter.drawPixmap(0, top, self.rendered_pixmap)

    def paint_background(self, painter):
        br = 12  # border_radius
        bw = 1  # border_width
        pal = self.palette()
        c = pal.color(pal.Window)
        c.setAlphaF(0.9)
        p = QPainterPath()
        p.addRoundedRect(QRectF(self.rect()), br, br)
        painter.fillPath(p, c)
        p.addRoundedRect(QRectF(self.rect()).adjusted(bw, bw, -bw, -bw), br, br)
        painter.fillPath(p, pal.color(pal.WindowText))
Exemplo n.º 5
0
class RunAction(QDialog):

    update_current_signal = pyqtSignal(object, object, object)
    update_overall_signal = pyqtSignal(object, object, object)
    finish_signal = pyqtSignal()

    def __init__(self, title, err_msg, action, parent=None):
        QDialog.__init__(self, parent)
        self.setWindowTitle(_('Working please wait...'))
        self.title, self.action, self.tb, self.err_msg = title, action, None, err_msg
        self.abort = Event()
        self.setup_ui()
        t = Thread(name='ExImWorker', target=self.run_action)
        t.daemon = True
        t.start()

    def setup_ui(self):
        self.l = l = QGridLayout(self)
        self.bb = QDialogButtonBox(self)
        self.bb.setStandardButtons(self.bb.Cancel)
        self.bb.rejected.connect(self.reject)

        self.la1 = la = QLabel('<h2>' + self.title)
        l.addWidget(la, 0, 0, 1, -1)
        self.la2 = la = QLabel(_('Total:'))
        l.addWidget(la, l.rowCount(), 0)
        self.overall = p = QProgressBar(self)
        p.setMinimum(0), p.setValue(0), p.setMaximum(0)
        p.setMinimumWidth(450)
        l.addWidget(p, l.rowCount()-1, 1)
        self.omsg = la = QLabel(self)
        la.setMaximumWidth(450)
        l.addWidget(la, l.rowCount(), 1)
        self.la3 = la = QLabel(_('Current:'))
        l.addWidget(la, l.rowCount(), 0)
        self.current = p = QProgressBar(self)
        p.setMinimum(0), p.setValue(0), p.setMaximum(0)
        l.addWidget(p, l.rowCount()-1, 1)
        self.cmsg = la = QLabel(self)
        la.setMaximumWidth(450)
        l.addWidget(la, l.rowCount(), 1)
        l.addWidget(self.bb, l.rowCount(), 0, 1, -1)
        self.update_current_signal.connect(self.update_current, type=Qt.QueuedConnection)
        self.update_overall_signal.connect(self.update_overall, type=Qt.QueuedConnection)
        self.finish_signal.connect(self.finish_processing, type=Qt.QueuedConnection)

    def update_overall(self, msg, count, total):
        self.overall.setMaximum(total), self.overall.setValue(count)
        self.omsg.setText(msg)

    def update_current(self, msg, count, total):
        self.current.setMaximum(total), self.current.setValue(count)
        self.cmsg.setText(msg)

    def reject(self):
        self.abort.set()
        self.bb.button(self.bb.Cancel).setEnabled(False)

    def finish_processing(self):
        if self.abort.is_set():
            return QDialog.reject(self)
        if self.tb is not None:
            error_dialog(self, _('Failed'), self.err_msg + ' ' + _('Click "Show Details" for more information.'),
                            det_msg=self.tb, show=True)
        self.accept()

    def run_action(self):
        try:
            self.action(abort=self.abort, progress1=self.update_overall_signal.emit, progress2=self.update_current_signal.emit)
        except Exception:
            import traceback
            self.tb = traceback.format_exc()
        self.finish_signal.emit()
Exemplo n.º 6
0
class ProceedQuestion(QDialog):

    ask_question = pyqtSignal(object, object, object)

    def __init__(self, parent):
        QDialog.__init__(self, parent)
        self.setAttribute(Qt.WA_DeleteOnClose, False)
        self.setWindowIcon(QIcon(I('dialog_question.png')))

        self.questions = []

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

        self.icon_label = ic = QLabel(self)
        ic.setPixmap(QPixmap(I('dialog_question.png')))
        self.msg_label = msg = QLabel('some random filler text')
        msg.setWordWrap(True)
        ic.setMaximumWidth(110)
        ic.setMaximumHeight(100)
        ic.setScaledContents(True)
        ic.setStyleSheet('QLabel { margin-right: 10px }')
        self.bb = QDialogButtonBox()
        self.bb.accepted.connect(self.accept)
        self.bb.rejected.connect(self.reject)
        self.log_button = self.bb.addButton(_('View log'), self.bb.ActionRole)
        self.log_button.setIcon(QIcon(I('debug.png')))
        self.log_button.clicked.connect(self.show_log)
        self.copy_button = self.bb.addButton(_('&Copy to clipboard'),
                self.bb.ActionRole)
        self.copy_button.clicked.connect(self.copy_to_clipboard)
        self.action_button = self.bb.addButton('', self.bb.ActionRole)
        self.action_button.clicked.connect(self.action_clicked)
        self.show_det_msg = _('Show &details')
        self.hide_det_msg = _('Hide &details')
        self.det_msg_toggle = self.bb.addButton(self.show_det_msg, self.bb.ActionRole)
        self.det_msg_toggle.clicked.connect(self.toggle_det_msg)
        self.det_msg_toggle.setToolTip(
                _('Show detailed information about this error'))
        self.det_msg = QPlainTextEdit(self)
        self.det_msg.setReadOnly(True)
        self.bb.setStandardButtons(self.bb.Yes|self.bb.No|self.bb.Ok)
        self.bb.button(self.bb.Yes).setDefault(True)

        self.checkbox = QCheckBox('', self)

        l.addWidget(ic, 0, 0, 1, 1)
        l.addWidget(msg, 0, 1, 1, 1)
        l.addWidget(self.checkbox, 1, 0, 1, 2)
        l.addWidget(self.det_msg, 2, 0, 1, 2)
        l.addWidget(self.bb, 3, 0, 1, 2)

        self.ask_question.connect(self.do_ask_question,
                type=Qt.QueuedConnection)

    def copy_to_clipboard(self, *args):
        QApplication.clipboard().setText(
                'calibre, version %s\n%s: %s\n\n%s' %
                (__version__, unicode(self.windowTitle()),
                    unicode(self.msg_label.text()),
                    unicode(self.det_msg.toPlainText())))
        self.copy_button.setText(_('Copied'))

    def action_clicked(self):
        if self.questions:
            q = self.questions[0]
            self.questions[0] = q._replace(callback=q.action_callback)
        self.accept()

    def accept(self):
        if self.geom_pref:
            gprefs[self.geom_pref] = bytearray(self.saveGeometry())
        if self.questions:
            payload, callback, cancel_callback = self.questions[0][:3]
            self.questions = self.questions[1:]
            cb = None
            if self.checkbox.isVisible():
                cb = bool(self.checkbox.isChecked())
            self.ask_question.emit(callback, payload, cb)
        self.hide()

    def reject(self):
        if self.geom_pref:
            gprefs[self.geom_pref] = bytearray(self.saveGeometry())
        if self.questions:
            payload, callback, cancel_callback = self.questions[0][:3]
            self.questions = self.questions[1:]
            cb = None
            if self.checkbox.isVisible():
                cb = bool(self.checkbox.isChecked())
            self.ask_question.emit(cancel_callback, payload, cb)
        self.hide()

    def do_ask_question(self, callback, payload, checkbox_checked):
        if callable(callback):
            args = [payload]
            if checkbox_checked is not None:
                args.append(checkbox_checked)
            callback(*args)
        self.show_question()

    def toggle_det_msg(self, *args):
        vis = unicode(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):
        if self.geom_pref:
            geom = gprefs.get(self.geom_pref, None)
            if geom:
                self.restoreGeometry(geom)
                return
        sz = self.sizeHint() + QSize(100, 0)
        sz.setWidth(min(500, sz.width()))
        sz.setHeight(min(500, sz.height()))
        self.resize(sz)

    def show_question(self):
        if self.isVisible():
            return
        if self.questions:
            question = self.questions[0]
            self.msg_label.setText(question.msg)
            self.setWindowTitle(question.title)
            self.log_button.setVisible(bool(question.html_log))
            self.copy_button.setText(_('&Copy to clipboard'))
            self.copy_button.setVisible(bool(question.show_copy_button))
            self.action_button.setVisible(question.action_callback is not None)
            if question.action_callback is not None:
                self.action_button.setText(question.action_label or '')
                self.action_button.setIcon(
                    QIcon() if question.action_icon is None else question.action_icon)
            self.det_msg.setPlainText(question.det_msg or '')
            self.det_msg.setVisible(False)
            self.det_msg_toggle.setVisible(bool(question.det_msg))
            self.det_msg_toggle.setText(self.show_det_msg)
            self.checkbox.setVisible(question.checkbox_msg is not None)
            if question.checkbox_msg is not None:
                self.checkbox.setText(question.checkbox_msg)
                self.checkbox.setChecked(question.checkbox_checked)
            self.bb.button(self.bb.Ok).setVisible(question.show_ok)
            self.bb.button(self.bb.Yes).setVisible(not question.show_ok)
            self.bb.button(self.bb.No).setVisible(not question.show_ok)
            self.geom_pref = ('proceed question dialog:' + question.geom_pref) if question.geom_pref else None
            if question.show_det:
                self.toggle_det_msg()
            else:
                self.do_resize()
            self.show()
            button = self.action_button if question.focus_action and question.action_callback is not None else \
                (self.bb.button(self.bb.Ok) if question.show_ok else self.bb.button(self.bb.Yes))
            button.setDefault(True)
            button.setFocus(Qt.OtherFocusReason)

    def __call__(self, callback, payload, html_log, log_viewer_title, title,
            msg, det_msg='', show_copy_button=False, cancel_callback=None,
            log_is_file=False, checkbox_msg=None, checkbox_checked=False,
            action_callback=None, action_label=None, action_icon=None, focus_action=False,
            show_det=False, show_ok=False, geom_pref=None):
        '''
        A non modal popup that notifies the user that a background task has
        been completed. This class guarantees that only a single popup is
        visible at any one time. Other requests are queued and displayed after
        the user dismisses the current popup.

        :param callback: A callable that is called with payload if the user
        asks to proceed. Note that this is always called in the GUI thread.
        :param cancel_callback: A callable that is called with the payload if
        the users asks not to proceed.
        :param payload: Arbitrary object, passed to callback
        :param html_log: An HTML or plain text log
        :param log_viewer_title: The title for the log viewer window
        :param title: The title for this popup
        :param msg: The msg to display
        :param det_msg: Detailed message
        :param log_is_file: If True the html_log parameter is interpreted as
                            the path to a file on disk containing the log
                            encoded with utf-8
        :param checkbox_msg: If not None, a checkbox is displayed in the
                             dialog, showing this message. The callback is
                             called with both the payload and the state of the
                             checkbox as arguments.
        :param checkbox_checked: If True the checkbox is checked by default.
        :param action_callback: If not None, an extra button is added, which
                                when clicked will cause action_callback to be called
                                instead of callback. action_callback is called in
                                exactly the same way as callback.
        :param action_label: The text on the action button
        :param action_icon: The icon for the action button, must be a QIcon object or None
        :param focus_action: If True, the action button will be focused instead of the Yes button
        :param show_det: If True, the Detailed message will be shown initially
        :param show_ok: If True, OK will be shown instead of YES/NO
        :param geom_pref: String for preference name to preserve dialog box geometry

        '''
        question = Question(
            payload, callback, cancel_callback, title, msg, html_log,
            log_viewer_title, log_is_file, det_msg, show_copy_button,
            checkbox_msg, checkbox_checked, action_callback, action_label,
            action_icon, focus_action, show_det, show_ok, geom_pref)
        self.questions.append(question)
        self.show_question()

    def show_log(self):
        if self.questions:
            q = self.questions[0]
            log = q.html_log
            if q.log_is_file:
                with open(log, 'rb') as f:
                    log = f.read().decode('utf-8')
            self.log_viewer = ViewLog(q.log_viewer_title, log,
                        parent=self)
Exemplo n.º 7
0
class DemoDialog(QDialog):

    def __init__(self, gui, icon, do_user_config):
        QDialog.__init__(self, gui)
        self.gui = gui
        self.do_user_config = do_user_config

        self.db = gui.current_db

        self.l = QVBoxLayout()
        self.setLayout(self.l)

        self.setWindowTitle('Wiki Reader')
        self.setWindowIcon(icon)

        self.helpl = QLabel(
            'Enter the URL of a wikipedia article below. '
            'It will be downloaded and converted into an ebook.')
        self.helpl.setWordWrap(True)
        self.l.addWidget(self.helpl)

        self.w = QWidget(self)
        self.sa = QScrollArea(self)
        self.l.addWidget(self.sa)
        self.w.l = QVBoxLayout()
        self.w.setLayout(self.w.l)
        self.sa.setWidget(self.w)
        self.sa.setWidgetResizable(True)

        self.title = Title(self)
        self.w.l.addWidget(self.title)

        self.urls = [URL(self)]
        self.w.l.addWidget(self.urls[0])

        self.add_more_button = QPushButton(
            QIcon(I('plus.png')), 'Add another URL')
        self.l.addWidget(self.add_more_button)

        self.bb = QDialogButtonBox(self)
        self.bb.setStandardButtons(self.bb.Ok | self.bb.Cancel)
        self.bb.accepted.connect(self.accept)
        self.bb.rejected.connect(self.reject)
        self.l.addWidget(self.bb)
        self.book_button = b = self.bb.addButton(
            'Convert a Wiki&Book', self.bb.ActionRole)
        b.clicked.connect(self.wiki_book)

        self.add_more_button.clicked.connect(self.add_more)
        self.finished.connect(self.download)

        self.setMinimumWidth(500)
        self.resize(self.sizeHint())
        self.single_url = None

    def wiki_book(self):
        d = QDialog(self)
        d.l = l = QFormLayout(d)
        l.setFieldGrowthPolicy(l.ExpandingFieldsGrow)
        d.setWindowTitle('Enter WikiBook URL')
        d.la = la = QLabel(
            '<p>You can convert a pre-made WikiBook into a book here. '
            'Simply enter the URL to the WikiBook page. For a list of '
            'WikiBooks, see '
            '<a href="https://en.wikipedia.org/wiki/Special:PrefixIndex/Book:'
            '">here</a>.'
        )
        la.setMinimumWidth(400)
        la.setWordWrap(True)
        la.setOpenExternalLinks(True)
        l.addRow(la)
        d.le = le = QLineEdit(self)
        l.addRow('WikiBook &URL:', le)
        le.setText('https://')
        le.selectAll()
        d.bb = bb = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel, d)
        l.addRow(bb)
        bb.accepted.connect(d.accept), bb.rejected.connect(d.reject)
        if d.exec_() == d.Accepted:
            self.single_url = le.text()
            self.accept()

    def do_resize(self):
        a = self.sizeHint()
        b = self.w.sizeHint()
        h = min(400, b.height())
        self.resize(QSize(a.width(), h + 200))

    def scroll_to_bottom(self):
        v = self.sa.verticalScrollBar()
        v.setValue(v.maximum())

    def add_more(self):
        url = URL(self)
        self.urls.append(url)
        self.w.l.addWidget(url)
        QTimer.singleShot(0, self.do_resize)
        QTimer.singleShot(10, self.scroll_to_bottom)

    def about(self):
        # Get the about text from a file inside the plugin zip file
        # The get_resources function is a builtin function defined for all your
        # plugin code. It loads files from the plugin zip file. It returns
        # the bytes from the specified file.
        #
        # Note that if you are loading more than one file, for performance, you
        # should pass a list of names to get_resources. In this case,
        # get_resources will return a dictionary mapping names to bytes. Names
        # that are not found in the zip file will not be in the returned
        # dictionary.
        text = get_resources('about.txt')
        QMessageBox.about(self, 'About the Wiki Reader', text.decode('utf-8'))

    # def config(self):
    #     self.do_user_config(parent=self)
    # Apply the changes
    #     self.label.setText(prefs['hello_world_msg'])

    def download(self, retcode):
        if retcode != self.Accepted:
            return
        if self.single_url is None:
            urls = [x.url for x in self.urls]
            urls = [x.strip() for x in urls if x.strip()]
            urls = [
                ('http://' + x) if not urlparse(x).scheme else x for x in urls]
        else:
            urls = self.single_url
            self.single_url = None
        args, fmt, temp_files = get_recipe(urls, self.title.title)
        job = self.gui.job_manager.run_job(
            Dispatcher(self.fetched), 'gui_convert', args=args,
            description='Fetch article from Wikipedia')
        job.extra_conversion_args = (temp_files, fmt)
        # don't prompt if all OK
        # if isinstance(urls, list):
        #     info_dialog(
        #         self, 'Downloading',
        #         'Downloading %d article(s) from Wikipedia. When the download'
        #         ' completes the book will be added to your calibre library.'
        #         % len(urls), show=True, show_copy_button=False)
        # else:
        #     info_dialog(
        #         self, 'Downloading',
        #         'Downloading book from Wikipedia. When the download'
        #         ' completes the book will be added to your calibre library.',
        #         show=True, show_copy_button=False)

    def fetched(self, job):
        if job.failed:
            return self.gui.job_exception(job)
        temp_files, fmt = job.extra_conversion_args
        fname = temp_files[0].name
        self.gui.iactions['Add Books']._add_books([fname], False)
        for f in temp_files[1:]:
            try:
                os.remove(f.name)
            except:
                pass
Exemplo n.º 8
0
class ProceedQuestion(QWidget):

    ask_question = pyqtSignal(object, object, object)

    @pyqtProperty(float)
    def show_fraction(self):
        return self._show_fraction

    @show_fraction.setter
    def show_fraction(self, val):
        self._show_fraction = max(0, min(1, float(val)))
        self.update()

    def __init__(self, parent):
        QWidget.__init__(self, parent)
        self.setVisible(False)
        parent.installEventFilter(self)

        self._show_fraction = 0.0
        self.show_animation = a = QPropertyAnimation(self, b"show_fraction", self)
        a.setDuration(1000), a.setEasingCurve(QEasingCurve.OutQuad)
        a.setStartValue(0.0), a.setEndValue(1.0)
        a.finished.connect(self.stop_show_animation)
        self.rendered_pixmap = None

        self.questions = []

        self.icon = ic = Icon(self)
        self.msg_label = msg = QLabel('some random filler text')
        msg.setWordWrap(True)
        self.bb = QDialogButtonBox()
        self.bb.accepted.connect(self.accept)
        self.bb.rejected.connect(self.reject)
        self.log_button = self.bb.addButton(_('View log'), self.bb.ActionRole)
        self.log_button.setIcon(QIcon(I('debug.png')))
        self.log_button.clicked.connect(self.show_log)
        self.copy_button = self.bb.addButton(_('&Copy to clipboard'),
                self.bb.ActionRole)
        self.copy_button.clicked.connect(self.copy_to_clipboard)
        self.action_button = self.bb.addButton('', self.bb.ActionRole)
        self.action_button.clicked.connect(self.action_clicked)
        self.show_det_msg = _('Show &details')
        self.hide_det_msg = _('Hide &details')
        self.det_msg_toggle = self.bb.addButton(self.show_det_msg, self.bb.ActionRole)
        self.det_msg_toggle.clicked.connect(self.toggle_det_msg)
        self.det_msg_toggle.setToolTip(
                _('Show detailed information about this error'))
        self.det_msg = PlainTextEdit(self)
        self.det_msg.setReadOnly(True)
        self.bb.setStandardButtons(self.bb.Yes|self.bb.No|self.bb.Ok)
        self.bb.button(self.bb.Yes).setDefault(True)
        self.title_label = title = QLabel('A dummy title')
        f = title.font()
        f.setBold(True)
        title.setFont(f)

        self.checkbox = QCheckBox('', self)

        self._l = l = QVBoxLayout(self)
        self._h = h = QHBoxLayout()
        self._v = v = QVBoxLayout()
        v.addWidget(title), v.addWidget(msg)
        h.addWidget(ic), h.addSpacing(10), h.addLayout(v), l.addLayout(h)
        l.addSpacing(5)
        l.addWidget(self.checkbox)
        l.addWidget(self.det_msg)
        l.addWidget(self.bb)

        self.ask_question.connect(self.do_ask_question,
                type=Qt.QueuedConnection)
        self.setFocusPolicy(Qt.NoFocus)
        for child in self.findChildren(QWidget):
            child.setFocusPolicy(Qt.NoFocus)
        self.setFocusProxy(self.parent())
        self.resize_timer = t = QTimer(self)
        t.setSingleShot(True), t.setInterval(100), t.timeout.connect(self.parent_resized)

    def eventFilter(self, obj, ev):
        if ev.type() == ev.Resize and self.isVisible():
            self.resize_timer.start()
        return False

    def parent_resized(self):
        if self.isVisible():
            self.do_resize()

    def copy_to_clipboard(self, *args):
        QApplication.clipboard().setText(
                'calibre, version %s\n%s: %s\n\n%s' %
                (__version__, str(self.windowTitle()),
                    str(self.msg_label.text()),
                    str(self.det_msg.toPlainText())))
        self.copy_button.setText(_('Copied'))

    def action_clicked(self):
        if self.questions:
            q = self.questions[0]
            self.questions[0] = q._replace(callback=q.action_callback)
        self.accept()

    def accept(self):
        if self.questions:
            payload, callback, cancel_callback = self.questions[0][:3]
            self.questions = self.questions[1:]
            cb = None
            if self.checkbox.isVisible():
                cb = bool(self.checkbox.isChecked())
            self.ask_question.emit(callback, payload, cb)
        self.hide()

    def reject(self):
        if self.questions:
            payload, callback, cancel_callback = self.questions[0][:3]
            self.questions = self.questions[1:]
            cb = None
            if self.checkbox.isVisible():
                cb = bool(self.checkbox.isChecked())
            self.ask_question.emit(cancel_callback, payload, cb)
        self.hide()

    def do_ask_question(self, callback, payload, checkbox_checked):
        if callable(callback):
            args = [payload]
            if checkbox_checked is not None:
                args.append(checkbox_checked)
            callback(*args)
        self.show_question()

    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):
        sz = self.sizeHint()
        sz.setWidth(min(self.parent().width(), sz.width()))
        sb = self.parent().statusBar().height() + 10
        sz.setHeight(min(self.parent().height() - sb, sz.height()))
        self.resize(sz)
        self.position_widget()

    def show_question(self):
        if not self.questions:
            return
        if not self.isVisible():
            question = self.questions[0]
            self.msg_label.setText(question.msg)
            self.icon.set_icon(question.icon)
            self.title_label.setText(question.title)
            self.log_button.setVisible(bool(question.html_log))
            self.copy_button.setText(_('&Copy to clipboard'))
            if question.action_callback is not None:
                self.action_button.setText(question.action_label or '')
                self.action_button.setIcon(
                    QIcon() if question.action_icon is None else question.action_icon)
            # Force the button box to relayout its buttons, as button text
            # might have changed
            self.bb.setOrientation(Qt.Vertical), self.bb.setOrientation(Qt.Horizontal)
            self.det_msg.setPlainText(question.det_msg or '')
            self.det_msg.setVisible(False)
            self.det_msg_toggle.setVisible(bool(question.det_msg))
            self.det_msg_toggle.setText(self.show_det_msg)
            self.checkbox.setVisible(question.checkbox_msg is not None)
            if question.checkbox_msg is not None:
                self.checkbox.setText(question.checkbox_msg)
                self.checkbox.setChecked(question.checkbox_checked)
            self.bb.button(self.bb.Ok).setVisible(question.show_ok)
            self.bb.button(self.bb.Yes).setVisible(not question.show_ok)
            self.bb.button(self.bb.No).setVisible(not question.show_ok)
            self.copy_button.setVisible(bool(question.show_copy_button))
            self.action_button.setVisible(question.action_callback is not None)
            self.toggle_det_msg() if question.show_det else self.do_resize()
            self.show_widget()
            button = self.action_button if question.focus_action and question.action_callback is not None else \
                (self.bb.button(self.bb.Ok) if question.show_ok else self.bb.button(self.bb.Yes))
            button.setDefault(True)
            self.raise_()
            self.start_show_animation()

    def start_show_animation(self):
        if self.rendered_pixmap is not None:
            return

        dpr = getattr(self, 'devicePixelRatioF', self.devicePixelRatio)()
        p = QImage(dpr * self.size(), QImage.Format_ARGB32_Premultiplied)
        p.setDevicePixelRatio(dpr)
        self.render(p)
        self.rendered_pixmap = QPixmap.fromImage(p)
        self.original_visibility = v = []
        for child in self.findChildren(QWidget):
            if child.isVisible():
                child.setVisible(False)
                v.append(child)
        self.show_animation.start()

    def stop_show_animation(self):
        self.rendered_pixmap = None
        [c.setVisible(True) for c in getattr(self, 'original_visibility', ())]
        self.update()
        for child in self.findChildren(QWidget):
            child.update()
            if hasattr(child, 'viewport'):
                child.viewport().update()

    def position_widget(self):
        geom = self.parent().geometry()
        x = geom.width() - self.width() - 5
        sb = self.parent().statusBar()
        if sb is None:
            y = geom.height() - self.height()
        else:
            y = sb.geometry().top() - self.height()
        self.move(x, y)

    def show_widget(self):
        self.show()
        self.position_widget()

    def dummy_question(self, action_label=None):
        self(lambda *args:args, (), 'dummy log', 'Log Viewer', 'A Dummy Popup',
             'This is a dummy popup to easily test things, with a long line of text that should wrap. '
             'This is a dummy popup to easily test things, with a long line of text that should wrap',
             checkbox_msg='A dummy checkbox',
             action_callback=lambda *args: args, action_label=action_label or 'An action')

    def __call__(self, callback, payload, html_log, log_viewer_title, title,
            msg, det_msg='', show_copy_button=False, cancel_callback=None,
            log_is_file=False, checkbox_msg=None, checkbox_checked=False,
            action_callback=None, action_label=None, action_icon=None, focus_action=False,
            show_det=False, show_ok=False, icon=None, log_viewer_unique_name=None, **kw):
        '''
        A non modal popup that notifies the user that a background task has
        been completed. This class guarantees that only a single popup is
        visible at any one time. Other requests are queued and displayed after
        the user dismisses the current popup.

        :param callback: A callable that is called with payload if the user
        asks to proceed. Note that this is always called in the GUI thread.
        :param cancel_callback: A callable that is called with the payload if
        the users asks not to proceed.
        :param payload: Arbitrary object, passed to callback
        :param html_log: An HTML or plain text log
        :param log_viewer_title: The title for the log viewer window
        :param title: The title for this popup
        :param msg: The msg to display
        :param det_msg: Detailed message
        :param log_is_file: If True the html_log parameter is interpreted as
                            the path to a file on disk containing the log
                            encoded with utf-8
        :param checkbox_msg: If not None, a checkbox is displayed in the
                             dialog, showing this message. The callback is
                             called with both the payload and the state of the
                             checkbox as arguments.
        :param checkbox_checked: If True the checkbox is checked by default.
        :param action_callback: If not None, an extra button is added, which
                                when clicked will cause action_callback to be called
                                instead of callback. action_callback is called in
                                exactly the same way as callback.
        :param action_label: The text on the action button
        :param action_icon: The icon for the action button, must be a QIcon object or None
        :param focus_action: If True, the action button will be focused instead of the Yes button
        :param show_det: If True, the Detailed message will be shown initially
        :param show_ok: If True, OK will be shown instead of YES/NO
        :param icon: The icon to be used for this popop (defaults to question mark). Can be either a QIcon or a string to be used with I()
        :log_viewer_unique_name: If set, ViewLog will remember/reuse its size for this name in calibre.gui2.gprefs
        '''
        question = Question(
            payload, callback, cancel_callback, title, msg, html_log,
            log_viewer_title, log_is_file, det_msg, show_copy_button,
            checkbox_msg, checkbox_checked, action_callback, action_label,
            action_icon, focus_action, show_det, show_ok, icon, log_viewer_unique_name)
        self.questions.append(question)
        self.show_question()

    def show_log(self):
        if self.questions:
            q = self.questions[0]
            log = q.html_log
            if q.log_is_file:
                with open(log, 'rb') as f:
                    log = f.read().decode('utf-8')
            self.log_viewer = ViewLog(q.log_viewer_title, log,
                        parent=self, unique_name=q.log_viewer_unique_name)

    def paintEvent(self, ev):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing, True)
        painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
        try:
            if self.rendered_pixmap is None:
                self.paint_background(painter)
            else:
                self.animated_paint(painter)
        finally:
            painter.end()

    def animated_paint(self, painter):
        top = (1 - self._show_fraction) * self.height()
        painter.drawPixmap(0, top, self.rendered_pixmap)

    def paint_background(self, painter):
        br = 12  # border_radius
        bw = 1  # border_width
        pal = self.palette()
        c = pal.color(pal.Window)
        c.setAlphaF(0.9)
        p = QPainterPath()
        p.addRoundedRect(QRectF(self.rect()), br, br)
        painter.fillPath(p, c)
        p.addRoundedRect(QRectF(self.rect()).adjusted(bw, bw, -bw, -bw), br, br)
        painter.fillPath(p, pal.color(pal.WindowText))
Exemplo n.º 9
0
class Config(QDialog):
    '''
    Configuration dialog for single book conversion. If accepted, has the
    following important attributes

    output_format - Output format (without a leading .)
    input_format  - Input format (without a leading .)
    opf_path - Path to OPF file with user specified metadata
    cover_path - Path to user specified cover (can be None)
    recommendations - A pickled list of 3 tuples in the same format as the
    recommendations member of the Input/Output plugins.
    '''
    def __init__(self,
                 parent,
                 db,
                 book_id,
                 preferred_input_format=None,
                 preferred_output_format=None):
        QDialog.__init__(self, parent)
        self.widgets = []
        self.setupUi()
        self.opt_individual_saved_settings.setVisible(False)
        self.db, self.book_id = db, book_id

        self.setup_input_output_formats(self.db, self.book_id,
                                        preferred_input_format,
                                        preferred_output_format)
        self.setup_pipeline()

        self.input_formats.currentIndexChanged[native_string_type].connect(
            self.setup_pipeline)
        self.output_formats.currentIndexChanged[native_string_type].connect(
            self.setup_pipeline)
        self.groups.setSpacing(5)
        self.groups.activated[(QModelIndex)].connect(self.show_pane)
        self.groups.clicked[(QModelIndex)].connect(self.show_pane)
        self.groups.entered[(QModelIndex)].connect(self.show_group_help)
        rb = self.buttonBox.button(self.buttonBox.RestoreDefaults)
        rb.setText(_('Restore &defaults'))
        rb.clicked.connect(self.restore_defaults)
        self.groups.setMouseTracking(True)
        geom = gprefs.get('convert_single_dialog_geom', None)
        if geom:
            QApplication.instance().safe_restore_geometry(self, geom)
        else:
            self.resize(self.sizeHint())

    def setupUi(self):
        self.setObjectName("Dialog")
        self.resize(1024, 700)
        self.setWindowIcon(QIcon(I('convert.png')))
        self.gridLayout = QGridLayout(self)
        self.gridLayout.setObjectName("gridLayout")
        self.horizontalLayout = QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.input_label = QLabel(self)
        self.input_label.setObjectName("input_label")
        self.horizontalLayout.addWidget(self.input_label)
        self.input_formats = QComboBox(self)
        self.input_formats.setSizeAdjustPolicy(
            QComboBox.AdjustToMinimumContentsLengthWithIcon)
        self.input_formats.setMinimumContentsLength(5)
        self.input_formats.setObjectName("input_formats")
        self.horizontalLayout.addWidget(self.input_formats)
        self.opt_individual_saved_settings = QCheckBox(self)
        self.opt_individual_saved_settings.setObjectName(
            "opt_individual_saved_settings")
        self.horizontalLayout.addWidget(self.opt_individual_saved_settings)
        spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding,
                                 QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem)
        self.label_2 = QLabel(self)
        self.label_2.setObjectName("label_2")
        self.horizontalLayout.addWidget(self.label_2)
        self.output_formats = QComboBox(self)
        self.output_formats.setSizeAdjustPolicy(
            QComboBox.AdjustToMinimumContentsLengthWithIcon)
        self.output_formats.setMinimumContentsLength(5)
        self.output_formats.setObjectName("output_formats")
        self.horizontalLayout.addWidget(self.output_formats)
        self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 2)
        self.groups = QListView(self)
        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(1)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.groups.sizePolicy().hasHeightForWidth())
        self.groups.setSizePolicy(sizePolicy)
        self.groups.setTabKeyNavigation(True)
        self.groups.setIconSize(QSize(48, 48))
        self.groups.setWordWrap(True)
        self.groups.setObjectName("groups")
        self.gridLayout.addWidget(self.groups, 1, 0, 3, 1)
        self.scrollArea = QScrollArea(self)
        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(4)
        sizePolicy.setVerticalStretch(10)
        sizePolicy.setHeightForWidth(
            self.scrollArea.sizePolicy().hasHeightForWidth())
        self.scrollArea.setSizePolicy(sizePolicy)
        self.scrollArea.setFrameShape(QFrame.NoFrame)
        self.scrollArea.setLineWidth(0)
        self.scrollArea.setWidgetResizable(True)
        self.scrollArea.setObjectName("scrollArea")
        self.page = QWidget()
        self.page.setObjectName("page")
        self.gridLayout.addWidget(self.scrollArea, 1, 1, 1, 1)
        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setOrientation(Qt.Horizontal)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel
                                          | QDialogButtonBox.Ok
                                          | QDialogButtonBox.RestoreDefaults)
        self.buttonBox.setObjectName("buttonBox")
        self.gridLayout.addWidget(self.buttonBox, 3, 1, 1, 1)
        self.help = QTextEdit(self)
        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.help.sizePolicy().hasHeightForWidth())
        self.help.setSizePolicy(sizePolicy)
        self.help.setMaximumHeight(80)
        self.help.setObjectName("help")
        self.gridLayout.addWidget(self.help, 2, 1, 1, 1)
        self.input_label.setBuddy(self.input_formats)
        self.label_2.setBuddy(self.output_formats)
        self.input_label.setText(_("&Input format:"))
        self.opt_individual_saved_settings.setText(
            _("Use &saved conversion settings for individual books"))
        self.label_2.setText(_("&Output format:"))

        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)

    def sizeHint(self):
        desktop = QCoreApplication.instance().desktop()
        geom = desktop.availableGeometry(self)
        nh, nw = max(300, geom.height() - 100), max(400, geom.width() - 70)
        return QSize(nw, nh)

    def restore_defaults(self):
        delete_specifics(self.db, self.book_id)
        self.setup_pipeline()

    @property
    def input_format(self):
        return unicode_type(self.input_formats.currentText()).lower()

    @property
    def output_format(self):
        return unicode_type(self.output_formats.currentText()).lower()

    @property
    def manually_fine_tune_toc(self):
        for w in self.widgets:
            if hasattr(w, 'manually_fine_tune_toc'):
                return w.manually_fine_tune_toc.isChecked()

    def setup_pipeline(self, *args):
        oidx = self.groups.currentIndex().row()
        input_format = self.input_format
        output_format = self.output_format
        self.plumber = create_dummy_plumber(input_format, output_format)

        def widget_factory(cls):
            return cls(self, self.plumber.get_option_by_name,
                       self.plumber.get_option_help, self.db, self.book_id)

        self.mw = widget_factory(MetadataWidget)
        self.setWindowTitle(
            _('Convert') + ' ' + unicode_type(self.mw.title.text()))
        lf = widget_factory(LookAndFeelWidget)
        hw = widget_factory(HeuristicsWidget)
        sr = widget_factory(SearchAndReplaceWidget)
        ps = widget_factory(PageSetupWidget)
        sd = widget_factory(StructureDetectionWidget)
        toc = widget_factory(TOCWidget)
        from calibre.gui2.actions.toc_edit import SUPPORTED
        toc.manually_fine_tune_toc.setVisible(
            output_format.upper() in SUPPORTED)
        debug = widget_factory(DebugWidget)

        output_widget = self.plumber.output_plugin.gui_configuration_widget(
            self, self.plumber.get_option_by_name,
            self.plumber.get_option_help, self.db, self.book_id)
        input_widget = self.plumber.input_plugin.gui_configuration_widget(
            self, self.plumber.get_option_by_name,
            self.plumber.get_option_help, self.db, self.book_id)

        self.break_cycles()
        self.widgets = widgets = [self.mw, lf, hw, ps, sd, toc, sr]
        if input_widget is not None:
            widgets.append(input_widget)
        if output_widget is not None:
            widgets.append(output_widget)
        widgets.append(debug)
        for w in widgets:
            w.set_help_signal.connect(self.help.setPlainText)
            w.setVisible(False)

        self._groups_model = GroupModel(widgets)
        self.groups.setModel(self._groups_model)

        idx = oidx if -1 < oidx < self._groups_model.rowCount() else 0
        self.groups.setCurrentIndex(self._groups_model.index(idx))
        self.show_pane(idx)
        try:
            shutil.rmtree(self.plumber.archive_input_tdir, ignore_errors=True)
        except Exception:
            pass

    def setup_input_output_formats(self, db, book_id, preferred_input_format,
                                   preferred_output_format):
        if preferred_output_format:
            preferred_output_format = preferred_output_format.upper()
        output_formats = get_output_formats(preferred_output_format)
        input_format, input_formats = get_input_format_for_book(
            db, book_id, preferred_input_format)
        preferred_output_format = preferred_output_format if \
            preferred_output_format in output_formats else \
            sort_formats_by_preference(output_formats,
                    [prefs['output_format']])[0]
        self.input_formats.addItems(
            (unicode_type(x.upper()) for x in input_formats))
        self.output_formats.addItems(
            (unicode_type(x.upper()) for x in output_formats))
        self.input_formats.setCurrentIndex(input_formats.index(input_format))
        self.output_formats.setCurrentIndex(
            output_formats.index(preferred_output_format))

    def show_pane(self, index):
        if hasattr(index, 'row'):
            index = index.row()
        ow = self.scrollArea.takeWidget()
        if ow:
            ow.setParent(self)
        for i, w in enumerate(self.widgets):
            if i == index:
                self.scrollArea.setWidget(w)
                w.show()
            else:
                w.setVisible(False)

    def accept(self):
        recs = GuiRecommendations()
        for w in self._groups_model.widgets:
            if not w.pre_commit_check():
                return
            x = w.commit(save_defaults=False)
            recs.update(x)
        self.opf_file, self.cover_file = self.mw.opf_file, self.mw.cover_file
        self._recommendations = recs
        if self.db is not None:
            recs['gui_preferred_input_format'] = self.input_format
            save_specifics(self.db, self.book_id, recs)
        self.break_cycles()
        QDialog.accept(self)

    def reject(self):
        self.break_cycles()
        QDialog.reject(self)

    def done(self, r):
        if self.isVisible():
            gprefs['convert_single_dialog_geom'] = \
                bytearray(self.saveGeometry())
        return QDialog.done(self, r)

    def break_cycles(self):
        for w in self.widgets:
            w.break_cycles()

    @property
    def recommendations(self):
        recs = [(k, v, OptionRecommendation.HIGH)
                for k, v in self._recommendations.items()]
        return recs

    def show_group_help(self, index):
        widget = self._groups_model.widgets[index.row()]
        self.help.setPlainText(widget.HELP)
Exemplo n.º 10
0
class RunAction(QDialog):

    update_current_signal = pyqtSignal(object, object, object)
    update_overall_signal = pyqtSignal(object, object, object)
    finish_signal = pyqtSignal()

    def __init__(self, title, err_msg, action, parent=None):
        QDialog.__init__(self, parent)
        self.setWindowTitle(_('Working please wait...'))
        self.title, self.action, self.tb, self.err_msg = title, action, None, err_msg
        self.abort = Event()
        self.setup_ui()
        t = Thread(name='ExImWorker', target=self.run_action)
        t.daemon = True
        t.start()

    def setup_ui(self):
        self.l = l = QGridLayout(self)
        self.bb = QDialogButtonBox(self)
        self.bb.setStandardButtons(self.bb.Cancel)
        self.bb.rejected.connect(self.reject)

        self.la1 = la = QLabel('<h2>' + self.title)
        l.addWidget(la, 0, 0, 1, -1)
        self.la2 = la = QLabel(_('Total:'))
        l.addWidget(la, l.rowCount(), 0)
        self.overall = p = QProgressBar(self)
        p.setMinimum(0), p.setValue(0), p.setMaximum(0)
        p.setMinimumWidth(450)
        l.addWidget(p, l.rowCount() - 1, 1)
        self.omsg = la = QLabel(self)
        la.setMaximumWidth(450)
        l.addWidget(la, l.rowCount(), 1)
        self.la3 = la = QLabel(_('Current:'))
        l.addWidget(la, l.rowCount(), 0)
        self.current = p = QProgressBar(self)
        p.setMinimum(0), p.setValue(0), p.setMaximum(0)
        l.addWidget(p, l.rowCount() - 1, 1)
        self.cmsg = la = QLabel(self)
        la.setMaximumWidth(450)
        l.addWidget(la, l.rowCount(), 1)
        l.addWidget(self.bb, l.rowCount(), 0, 1, -1)
        self.update_current_signal.connect(self.update_current,
                                           type=Qt.QueuedConnection)
        self.update_overall_signal.connect(self.update_overall,
                                           type=Qt.QueuedConnection)
        self.finish_signal.connect(self.finish_processing,
                                   type=Qt.QueuedConnection)

    def update_overall(self, msg, count, total):
        self.overall.setMaximum(total), self.overall.setValue(count)
        self.omsg.setText(msg)

    def update_current(self, msg, count, total):
        self.current.setMaximum(total), self.current.setValue(count)
        self.cmsg.setText(msg)

    def reject(self):
        self.abort.set()
        self.bb.button(self.bb.Cancel).setEnabled(False)

    def finish_processing(self):
        if self.abort.is_set():
            return QDialog.reject(self)
        if self.tb is not None:
            error_dialog(self,
                         _('Failed'),
                         self.err_msg + ' ' +
                         _('Click "Show Details" for more information.'),
                         det_msg=self.tb,
                         show=True)
        self.accept()

    def run_action(self):
        try:
            self.action(abort=self.abort,
                        progress1=self.update_overall_signal.emit,
                        progress2=self.update_current_signal.emit)
        except Exception:
            import traceback
            self.tb = traceback.format_exc()
        self.finish_signal.emit()
Exemplo n.º 11
0
def show_config_widget(category,
                       name,
                       gui=None,
                       show_restart_msg=False,
                       parent=None,
                       never_shutdown=False):
    '''
    Show the preferences plugin identified by category and name

    :param gui: gui instance, if None a hidden gui is created
    :param show_restart_msg: If True and the preferences plugin indicates a
    restart is required, show a message box telling the user to restart
    :param parent: The parent of the displayed dialog

    :return: True iff a restart is required for the changes made by the user to
    take effect
    '''
    from calibre.gui2 import gprefs
    pl = get_plugin(category, name)
    d = ConfigDialog(parent)
    d.resize(750, 550)
    conf_name = 'config_widget_dialog_geometry_%s_%s' % (category, name)
    geom = gprefs.get(conf_name, None)
    d.setWindowTitle(_('Configure ') + pl.gui_name)
    d.setWindowIcon(QIcon(I('config.png')))
    bb = QDialogButtonBox(d)
    bb.setStandardButtons(bb.Apply | bb.Cancel | bb.RestoreDefaults)
    bb.accepted.connect(d.accept)
    bb.rejected.connect(d.reject)
    w = pl.create_widget(d)
    d.set_widget(w)
    bb.button(bb.RestoreDefaults).clicked.connect(w.restore_defaults)
    bb.button(bb.RestoreDefaults).setEnabled(w.supports_restoring_to_defaults)
    bb.button(bb.Apply).setEnabled(False)
    bb.button(bb.Apply).clicked.connect(d.accept)

    def onchange():
        b = bb.button(bb.Apply)
        b.setEnabled(True)
        b.setDefault(True)
        b.setAutoDefault(True)

    w.changed_signal.connect(onchange)
    bb.button(bb.Cancel).setFocus(True)
    l = QVBoxLayout()
    d.setLayout(l)
    l.addWidget(w)
    l.addWidget(bb)
    mygui = gui is None
    if gui is None:
        gui = init_gui()
        mygui = True
    w.genesis(gui)
    w.initialize()
    if geom is not None:
        d.restoreGeometry(geom)
    d.exec_()
    geom = bytearray(d.saveGeometry())
    gprefs[conf_name] = geom
    rr = getattr(d, 'restart_required', False)
    if show_restart_msg and rr:
        from calibre.gui2 import warning_dialog
        warning_dialog(gui, 'Restart required', 'Restart required', show=True)
    if mygui and not never_shutdown:
        gui.shutdown()
    return rr