Ejemplo n.º 1
0
    def __init__(self, parent=None,):
        QDialog.__init__(self, parent)
        self.parent = parent
        self.setWindowTitle(u"{0} {1}: Add New EInk Kindle Serial Number".format(PLUGIN_NAME, PLUGIN_VERSION))
        layout = QVBoxLayout(self)
        self.setLayout(layout)

        data_group_box = QGroupBox(u"", self)
        layout.addWidget(data_group_box)
        data_group_box_layout = QVBoxLayout()
        data_group_box.setLayout(data_group_box_layout)

        key_group = QHBoxLayout()
        data_group_box_layout.addLayout(key_group)
        key_group.addWidget(QLabel(u"EInk Kindle Serial Number:", self))
        self.key_ledit = QLineEdit("", self)
        self.key_ledit.setToolTip(u"Enter an eInk Kindle serial number. EInk Kindle serial numbers are 16 characters long and usually start with a 'B' or a '9'. Kindle Serial Numbers are case-sensitive, so be sure to enter the upper and lower case letters unchanged.")
        key_group.addWidget(self.key_ledit)

        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        layout.addWidget(self.button_box)

        self.resize(self.sizeHint())
Ejemplo n.º 2
0
    def __init__(self, parent, friendly_name, rac):
        #self.dialog = QDialog(parent.gui)
        QDialog.__init__(self, parent.gui)
        self.parent = parent
        self.opts = parent.opts
        self.rac = rac
        parent_loc = self.parent.gui.pos()
        self.move(parent_loc.x(), parent_loc.y())
        self.setWindowTitle(rac.import_dialog_title)
        self.setWindowIcon(self.opts.icon)

        l = QVBoxLayout()
        self.setLayout(l)

        self.pte = PlainTextEdit(self.parent)
        self.pte.setPlainText(rac.initial_dialog_text)
        self.pte.setMinimumWidth(400)
        l.addWidget(self.pte)

        self.dialogButtonBox = QDialogButtonBox(QDialogButtonBox.Cancel|QDialogButtonBox.Help)
        self.import_button = self.dialogButtonBox.addButton(self.dialogButtonBox.Ok)
        self.import_button.setText('Import')
        self.dialogButtonBox.clicked.connect(self.import_annotations_dialog_clicked)
        l.addWidget(self.dialogButtonBox)

        self.rejected.connect(self.close)

        self.exec_()
        self.text = str(self.pte.toPlainText())
Ejemplo n.º 3
0
class HelpView(SizePersistedDialog):
    '''
    Modeless dialog for presenting HTML help content
    '''

    def __init__(self, parent, icon, prefs, html=None, page=None, title=''):
        self.prefs = prefs
        #QDialog.__init__(self, parent=parent)
        super(HelpView, self).__init__(parent, 'help_dialog')
        self.setWindowTitle(title)
        self.setWindowIcon(icon)
        self.l = QVBoxLayout(self)
        self.setLayout(self.l)

        self.wv = QWebView()
        if html is not None:
            self.wv.setHtml(html)
        elif page is not None:
            self.wv.load(QUrl(page))
        self.wv.setMinimumHeight(100)
        self.wv.setMaximumHeight(16777215)
        self.wv.setMinimumWidth(400)
        self.wv.setMaximumWidth(16777215)
        self.wv.setGeometry(0, 0, 400, 100)
        self.wv.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.l.addWidget(self.wv)

        # Sizing
        sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth())
        self.setSizePolicy(sizePolicy)
        self.resize_dialog()
Ejemplo n.º 4
0
class LogDialog(QDialog):

    # GUI definition
    def __init__(self, log, icon):
        '''
        :param log: The text to show in the dialog
        :param icon: The window icon
        '''
        QDialog.__init__(self)

        self.setWindowTitle(_('Prince log'))
        self.setWindowIcon(icon)

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

        monofont = QFont('')
        monofont.setStyleHint(QFont.TypeWriter)

        self.box = QPlainTextEdit()
        self.box.setPlainText(log)
        self.box.setStyleSheet('* { font-family: monospace }')
        self.box.setMinimumWidth(500)
        self.box.setLineWrapMode(QPlainTextEdit.NoWrap)
        self.box.setReadOnly(True)
        self.box.setToolTip(_('<qt>Console output from the last Prince run</qt>'))
        self.l.addWidget(self.box)
         
        self.buttons = QDialogButtonBox(QDialogButtonBox.Ok)
        self.l.addWidget(self.buttons)
        self.buttons.accepted.connect(self.accept)

        self.adjustSize()
Ejemplo n.º 5
0
 def message_dialog(self, msg):
     self.clear_dialog()
     self.dialog = dialog = WindowModalDialog(self.top_level_window(), _("Ledger Status"))
     l = QLabel(msg)
     vbox = QVBoxLayout(dialog)
     vbox.addWidget(l)
     dialog.show()
class PlotDialog(QDialog):
    
    def __init__(self, parent=None):
        super(PlotDialog, self).__init__(parent)
        
        self.resize(550, 350)

        #self.verticalLayout.addWidget(self.buttonBox)
        
    def init_plot(self, component, field):
        
        self.plot = DataPlot()
        self.plot.initData(component, field)
        self.plot.resize(500, 300)
        self.plot.show()
        
        self.verticalLayout = QVBoxLayout(self)
        self.verticalLayout.addWidget(self.plot)
    
    def get_plot(self):
        return self.plot
        
    def closeEvent(self, event):
        self.reject()
        super(PlotDialog, self).closeEvent(event)
Ejemplo n.º 7
0
    def setup_ui(self):
        from calibre.gui2.preferences.look_feel import DisplayedFields, move_field_up, move_field_down
        self.l = QVBoxLayout(self)
        self.field_display_order = fdo = QListView(self)
        self.model = DisplayedFields(self.db, fdo, pref_name='popup_book_display_fields')
        self.model.initialize()
        fdo.setModel(self.model)
        fdo.setAlternatingRowColors(True)
        del self.db
        self.l.addWidget(QLabel('Select displayed metadata'))
        h = QHBoxLayout()
        h.addWidget(fdo)
        v = QVBoxLayout()
        self.mub = b = QToolButton(self)
        b.clicked.connect(partial(move_field_up, fdo, self.model))
        b.setIcon(QIcon(I('arrow-up.png')))
        b.setToolTip(_('Move the selected field up'))
        v.addWidget(b), v.addStretch(10)
        self.mud = b = QToolButton(self)
        b.setIcon(QIcon(I('arrow-down.png')))
        b.setToolTip(_('Move the selected field down'))
        b.clicked.connect(partial(move_field_down, fdo, self.model))
        v.addWidget(b)
        h.addLayout(v)

        self.l.addLayout(h)
        self.l.addWidget(QLabel('<p>' + _(
            'Note that <b>comments</b> will always be displayed at the end, regardless of the order you assign here')))

        b = self.bb.addButton(_('Restore &defaults'), self.bb.ActionRole)
        b.clicked.connect(self.restore_defaults)
        self.l.addWidget(self.bb)
        self.setMinimumHeight(500)
Ejemplo n.º 8
0
    def view_server_logs(self):
        from calibre.library.server import log_access_file, log_error_file

        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(open(log_error_file, "rb").read().decode("utf8", "replace"))
        except IOError:
            el.setPlainText("No error log found")
        layout.addWidget(QLabel(_("Access log:")))
        al = QPlainTextEdit(d)
        layout.addWidget(al)
        try:
            al.setPlainText(open(log_access_file, "rb").read().decode("utf8", "replace"))
        except IOError:
            al.setPlainText("No access log found")
        bx = QDialogButtonBox(QDialogButtonBox.Ok)
        layout.addWidget(bx)
        bx.accepted.connect(d.accept)
        d.show()
Ejemplo n.º 9
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(
             lopen(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(
             lopen(log_access_file, 'rb').read().decode('utf8', 'replace')
         )
     except EnvironmentError:
         al.setPlainText('No access log found')
     bx = QDialogButtonBox(QDialogButtonBox.Ok)
     layout.addWidget(bx)
     bx.accepted.connect(d.accept)
     d.show()
Ejemplo n.º 10
0
    def __init__(self, parent, icon, text, over=True):
        QDialog.__init__(self, parent)
        self.state=None

        layout = QVBoxLayout(self)
        self.setLayout(layout)
        self.setWindowTitle('UnMerge Epub')

        label = QLabel(text)
        label.setOpenExternalLinks(True)
        label.setWordWrap(True)
        layout.addWidget(label)

        button_box = QDialogButtonBox(self)
        button = button_box.addButton(_("Add"), button_box.AcceptRole)
        button.clicked.connect(self.add)

        if over:
            button = button_box.addButton(_("Overwrite"), button_box.AcceptRole)
            button.clicked.connect(self.over)

        button = button_box.addButton(_("Discard"), button_box.AcceptRole)
        button.clicked.connect(self.discard)
        button_box.accepted.connect(self.accept)

        layout.addWidget(button_box)
Ejemplo n.º 11
0
    def __init__(self, parent=None,):
        QDialog.__init__(self, parent)
        self.parent = parent
        self.setWindowTitle(u"{0} {1}: Add New Mobipocket PID".format(PLUGIN_NAME, PLUGIN_VERSION))
        layout = QVBoxLayout(self)
        self.setLayout(layout)

        data_group_box = QGroupBox(u"", self)
        layout.addWidget(data_group_box)
        data_group_box_layout = QVBoxLayout()
        data_group_box.setLayout(data_group_box_layout)

        key_group = QHBoxLayout()
        data_group_box_layout.addLayout(key_group)
        key_group.addWidget(QLabel(u"PID:", self))
        self.key_ledit = QLineEdit("", self)
        self.key_ledit.setToolTip(u"Enter a Mobipocket PID. Mobipocket PIDs are 8 or 10 characters long. Mobipocket PIDs are case-sensitive, so be sure to enter the upper and lower case letters unchanged.")
        key_group.addWidget(self.key_ledit)

        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        layout.addWidget(self.button_box)

        self.resize(self.sizeHint())
Ejemplo n.º 12
0
class Comments(Base):

    def setup_ui(self, parent):
        self._box = QGroupBox(parent)
        self._box.setTitle('&'+self.col_metadata['name'])
        self._layout = QVBoxLayout()
        self._tb = CommentsEditor(self._box, toolbar_prefs_name=u'metadata-comments-editor-widget-hidden-toolbars')
        self._tb.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
        # self._tb.setTabChangesFocus(True)
        self._layout.addWidget(self._tb)
        self._box.setLayout(self._layout)
        self.widgets = [self._box]

    def setter(self, val):
        if not val or not val.strip():
            val = ''
        else:
            val = comments_to_html(val)
        self._tb.html = val
        self._tb.wyswyg_dirtied()

    def getter(self):
        val = unicode(self._tb.html).strip()
        if not val:
            val = None
        return val

    @dynamic_property
    def tab(self):
        def fget(self):
            return self._tb.tab

        def fset(self, val):
            self._tb.tab = val
        return property(fget=fget, fset=fset)
    def _init_controls(self):
        layout = QVBoxLayout(self)
        self.setLayout(layout)

        ml = QHBoxLayout()
        layout.addLayout(ml, 1)

        self.keys_list = QListWidget(self)
        self.keys_list.setSelectionMode(QAbstractItemView.SingleSelection)
        self.keys_list.setFixedWidth(150)
        self.keys_list.setAlternatingRowColors(True)
        ml.addWidget(self.keys_list)
        self.value_text = QTextEdit(self)
        self.value_text.setTabStopWidth(24)
        self.value_text.setReadOnly(False)
        ml.addWidget(self.value_text, 1)

        button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        button_box.accepted.connect(self._apply_changes)
        button_box.rejected.connect(self.reject)
        self.clear_button = button_box.addButton('Clear', QDialogButtonBox.ResetRole)
        self.clear_button.setIcon(get_icon('trash.png'))
        self.clear_button.setToolTip('Clear all settings for this plugin')
        self.clear_button.clicked.connect(self._clear_settings)
        layout.addWidget(button_box)
    def initialize(self):
        """
        Initialize User QWidget

        """

        main_layout = QVBoxLayout(self)
        self.setLayout(main_layout)
        main_layout.setContentsMargins(0, 0, 0, 0)

        main_layout.addWidget(get_logo_widget(self, _('User View')))

        user_widget = QWidget(self)
        user_layout = QGridLayout(user_widget)

        user_title = QLabel(_('User informations:'))
        user_title.setObjectName('itemtitle')
        user_layout.addWidget(user_title, 0, 0, 1, 2)
        user_layout.setAlignment(user_title, Qt.AlignCenter)

        # User QWidgets
        user_layout.addWidget(self.get_informations_widget(), 1, 0, 1, 1)
        user_layout.addWidget(self.get_notes_mail_widget(), 1, 1, 1, 1)
        user_layout.addWidget(self.get_notifications_widget(), 2, 0, 1, 2)

        main_layout.addWidget(user_widget)
        center_widget(self)
Ejemplo n.º 15
0
class ConfigWidget(QWidget):

    def __init__(self):
        QWidget.__init__(self)
        self.l = QVBoxLayout()
        self.setLayout(self.l)

        self.about_button = QPushButton('Help', self)
        self.about_button.clicked.connect(self.about)
        self.l.addWidget(self.about_button)

        self.notebookLabel = QLabel('evernote Notebook')
        self.l.addWidget(self.notebookLabel)

        self.notebookMsg = QLineEdit(self)
        self.notebookMsg.setText(prefs['notebook'])
        self.l.addWidget(self.notebookMsg)
        self.notebookLabel.setBuddy(self.notebookMsg)
        
        self.tagsCsvLabel = QLabel('evernote Tags CSV (ie calibre,mykindle)')
        self.l.addWidget(self.tagsCsvLabel)

        self.tagsCsvMsg = QLineEdit(self)
        self.tagsCsvMsg.setText(prefs['tagsCsv'])
        self.l.addWidget(self.tagsCsvMsg)
        self.tagsCsvLabel.setBuddy(self.tagsCsvMsg)

    def save_settings(self):
        prefs['notebook'] = unicode(self.notebookMsg.text())
        prefs['tagsCsv'] = unicode(self.tagsCsvMsg.text())

    def about(self):
        text = get_resources('about.txt')
        QMessageBox.about(self, 'About calibrebeam',
                text.decode('utf-8'))
Ejemplo n.º 16
0
 def __init__(self, parent=None):
     self.loaded_ruleset = None
     QWidget.__init__(self, parent)
     self.PREFS_OBJECT = JSONConfig('style-transform-rules')
     l = QVBoxLayout(self)
     self.rules_widget = w = Rules(self)
     w.changed.connect(self.changed.emit)
     l.addWidget(w)
     self.h = h = QHBoxLayout()
     l.addLayout(h)
     self.export_button = b = QPushButton(_('E&xport'), self)
     b.setToolTip(_('Export these rules to a file'))
     b.clicked.connect(self.export_rules)
     h.addWidget(b)
     self.import_button = b = QPushButton(_('&Import'), self)
     b.setToolTip(_('Import previously exported rules'))
     b.clicked.connect(self.import_rules)
     h.addWidget(b)
     self.test_button = b = QPushButton(_('&Test rules'), self)
     b.clicked.connect(self.test_rules)
     h.addWidget(b)
     h.addStretch(10)
     self.save_button = b = QPushButton(_('&Save'), self)
     b.setToolTip(_('Save this ruleset for later re-use'))
     b.clicked.connect(self.save_ruleset)
     h.addWidget(b)
     self.export_button = b = QPushButton(_('&Load'), self)
     self.load_menu = QMenu(self)
     b.setMenu(self.load_menu)
     b.setToolTip(_('Load a previously saved ruleset'))
     b.clicked.connect(self.load_ruleset)
     h.addWidget(b)
     self.build_load_menu()
    def initialize(self):
        """
        Intialize QWidget

        """

        self.timer.setInterval(30000)
        self.timer.start()
        self.timer.timeout.connect(self.send_datamanager_events)

        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)

        self.events_list.setDragDropMode(QAbstractItemView.DragOnly)
        self.events_list.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.events_list.setDropIndicatorShown(True)
        self.events_list.doubleClicked.connect(self.remove_event)
        self.events_list.setWordWrap(True)
        self.events_list.setIconSize(QSize(16, 16))

        self.add_event(
            'OK',
            _('Welcome %s, you are connected to Alignak Backend') %
            data_manager.database['user'].name,
            timer=True
        )

        layout.addWidget(self.events_list)
Ejemplo n.º 18
0
    def __init__(self, parent):
        super(CharacterDialog, self).__init__(parent)
        self.setWindowTitle(_("KeepKey Seed Recovery"))
        self.character_pos = 0
        self.word_pos = 0
        self.loop = QEventLoop()
        self.word_help = QLabel()
        self.char_buttons = []

        vbox = QVBoxLayout(self)
        vbox.addWidget(WWLabel(CHARACTER_RECOVERY))
        hbox = QHBoxLayout()
        hbox.addWidget(self.word_help)
        for i in range(4):
            char_button = CharacterButton('*')
            char_button.setMaximumWidth(36)
            self.char_buttons.append(char_button)
            hbox.addWidget(char_button)
        self.accept_button = CharacterButton(_("Accept Word"))
        self.accept_button.clicked.connect(partial(self.process_key, 32))
        self.rejected.connect(partial(self.loop.exit, 1))
        hbox.addWidget(self.accept_button)
        hbox.addStretch(1)
        vbox.addLayout(hbox)

        self.finished_button = QPushButton(_("Seed Entered"))
        self.cancel_button = QPushButton(_("Cancel"))
        self.finished_button.clicked.connect(partial(self.process_key,
                                                     Qt.Key_Return))
        self.cancel_button.clicked.connect(self.rejected)
        buttons = Buttons(self.finished_button, self.cancel_button)
        vbox.addSpacing(40)
        vbox.addLayout(buttons)
        self.refresh()
        self.show()
Ejemplo n.º 19
0
 def __init__(self, parent):
     QWidget.__init__(self, parent)
     l = QVBoxLayout(parent)
     l.addWidget(self)
     l.setContentsMargins(0, 0, 0, 0)
     l = QFormLayout(self)
     l.setContentsMargins(0, 0, 0, 0)
     l.setFieldGrowthPolicy(l.AllNonFixedFieldsGrow)
     self.choices = c = QComboBox()
     c.setMinimumContentsLength(30)
     for text, data in [
             (_('Search for the author on Goodreads'), 'search-goodreads'),
             (_('Search for the author on Amazon'), 'search-amzn'),
             (_('Search for the author in your calibre library'), 'search-calibre'),
             (_('Search for the author on Wikipedia'), 'search-wikipedia'),
             (_('Search for the author on Google Books'), 'search-google'),
             (_('Search for the book on Goodreads'), 'search-goodreads-book'),
             (_('Search for the book on Amazon'), 'search-amzn-book'),
             (_('Search for the book on Google Books'), 'search-google-book'),
             (_('Use a custom search URL'), 'url'),
     ]:
         c.addItem(text, data)
     l.addRow(_('Clicking on &author names should:'), c)
     self.custom_url = u = QLineEdit(self)
     u.textChanged.connect(self.changed_signal)
     u.setPlaceholderText(_('Enter the URL'))
     c.currentIndexChanged.connect(self.current_changed)
     l.addRow(u)
     self.current_changed()
     c.currentIndexChanged.connect(self.changed_signal)
Ejemplo n.º 20
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
Ejemplo n.º 21
0
class BlockingBusy(QDialog):

    def __init__(self, msg, parent=None, window_title=_('Working')):
        QDialog.__init__(self, parent)

        self._layout = QVBoxLayout()
        self.setLayout(self._layout)
        self.msg = QLabel(msg)
        # self.msg.setWordWrap(True)
        self.font = QFont()
        self.font.setPointSize(self.font.pointSize() + 8)
        self.msg.setFont(self.font)
        self.pi = ProgressIndicator(self)
        self.pi.setDisplaySize(100)
        self._layout.addWidget(self.pi, 0, Qt.AlignHCenter)
        self._layout.addSpacing(15)
        self._layout.addWidget(self.msg, 0, Qt.AlignHCenter)
        self.start()
        self.setWindowTitle(window_title)
        self.resize(self.sizeHint())

    def start(self):
        self.pi.startAnimation()

    def stop(self):
        self.pi.stopAnimation()

    def accept(self):
        self.stop()
        return QDialog.accept(self)

    def reject(self):
        pass  # Cannot cancel this dialog
Ejemplo n.º 22
0
class ConfigWidget(QWidget):

    def __init__(self, plugin_action):
        QWidget.__init__(self)
        self.plugin_action = plugin_action
        
        self.l = QVBoxLayout()
        self.setLayout(self.l)

        tab_widget = QTabWidget(self)
        self.l.addWidget(tab_widget)

        self.basic_tab = BasicTab(self, plugin_action)
        tab_widget.addTab(self.basic_tab, _('Basic'))

        self.searches_tab = SearchesTab(self, plugin_action)
        tab_widget.addTab(self.searches_tab, _('Searches'))

    def save_settings(self):
        prefs['checkreadinglistsync'] = self.basic_tab.checkreadinglistsync.isChecked()
        prefs['checkdups'] = self.basic_tab.checkdups.isChecked()
        prefs['checknotinlibrary'] = self.basic_tab.checknotinlibrary.isChecked()
        prefs['checknotondevice'] = self.basic_tab.checknotondevice.isChecked()

        prefs['checkdups_search'] = unicode(self.searches_tab.checkdups_search.text())
        prefs['checknotinlibrary_search'] = unicode(self.searches_tab.checknotinlibrary_search.text())
        prefs['checknotondevice_search'] = unicode(self.searches_tab.checknotondevice_search.text())

        prefs.save_to_db()
        
    def edit_shortcuts(self):
        self.save_settings()
        d = KeyboardConfigDialog(self.plugin_action.gui, self.plugin_action.action_spec[0])
        if d.exec_() == d.Accepted:
            self.plugin_action.gui.keyboard.finalize()
Ejemplo n.º 23
0
class Comments(Base):

    def setup_ui(self, parent):
        self._box = QGroupBox(parent)
        self._box.setTitle('&'+self.col_metadata['name'])
        self._layout = QVBoxLayout()
        self._tb = CommentsEditor(self._box)
        self._tb.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
        # self._tb.setTabChangesFocus(True)
        self._layout.addWidget(self._tb)
        self._box.setLayout(self._layout)
        self.widgets = [self._box]

    def setter(self, val):
        if not val or not val.strip():
            val = ''
        else:
            val = comments_to_html(val)
        self._tb.html = val
        self._tb.wyswyg_dirtied()

    def getter(self):
        val = unicode(self._tb.html).strip()
        if not val:
            val = None
        return val
    def get_text_widget(self):
        """
        Return text QWidget with QTextEdit

        :return: text QWidget
        :rtype: QWidget
        """

        text_widget = QWidget()
        text_widget.setObjectName('dialog')
        text_layout = QVBoxLayout()
        text_widget.setLayout(text_layout)

        self.text_edit.setPlaceholderText(_('type your text...'))
        self.text_edit.setText(self.old_text)
        text_layout.addWidget(self.text_edit)

        # Accept button
        accept_btn = QPushButton(_('Confirm'), self)
        accept_btn.clicked.connect(self.accept_text)
        accept_btn.setObjectName('valid')
        accept_btn.setMinimumHeight(30)
        text_layout.addWidget(accept_btn)

        return text_widget
Ejemplo n.º 25
0
    def __init__(self, parent=None,):
        QDialog.__init__(self, parent)
        self.parent = parent
        self.setWindowTitle(u"{0} {1}: Create New Barnes & Noble Key".format(PLUGIN_NAME, PLUGIN_VERSION))
        layout = QVBoxLayout(self)
        self.setLayout(layout)

        data_group_box = QGroupBox(u"", self)
        layout.addWidget(data_group_box)
        data_group_box_layout = QVBoxLayout()
        data_group_box.setLayout(data_group_box_layout)

        key_group = QHBoxLayout()
        data_group_box_layout.addLayout(key_group)
        key_group.addWidget(QLabel(u"Unique Key Name:", self))
        self.key_ledit = QLineEdit("", self)
        self.key_ledit.setToolTip(_(u"<p>Enter an identifying name for this new key.</p>" +
                                u"<p>It should be something that will help you remember " +
                                u"what personal information was used to create it."))
        key_group.addWidget(self.key_ledit)
        key_label = QLabel(_(''), self)
        key_label.setAlignment(Qt.AlignHCenter)
        data_group_box_layout.addWidget(key_label)

        name_group = QHBoxLayout()
        data_group_box_layout.addLayout(name_group)
        name_group.addWidget(QLabel(u"B&N/nook account email address:", self))
        self.name_ledit = QLineEdit(u"", self)
        self.name_ledit.setToolTip(_(u"<p>Enter your email address as it appears in your B&N " +
                                u"account.</p>" +
                                u"<p>It will only be used to generate this " +
                                u"key and won\'t be stored anywhere " +
                                u"in calibre or on your computer.</p>" +
                                u"<p>eg: [email protected]</p>"))
        name_group.addWidget(self.name_ledit)
        name_disclaimer_label = QLabel(_(u"(Will not be saved in configuration data)"), self)
        name_disclaimer_label.setAlignment(Qt.AlignHCenter)
        data_group_box_layout.addWidget(name_disclaimer_label)

        ccn_group = QHBoxLayout()
        data_group_box_layout.addLayout(ccn_group)
        ccn_group.addWidget(QLabel(u"B&N/nook account password:"******"", self)
        self.cc_ledit.setToolTip(_(u"<p>Enter the password " +
                                u"for your B&N account.</p>" +
                                u"<p>The password will only be used to generate this " +
                                u"key and won\'t be stored anywhere in " +
                                u"calibre or on your computer."))
        ccn_group.addWidget(self.cc_ledit)
        ccn_disclaimer_label = QLabel(_('(Will not be saved in configuration data)'), self)
        ccn_disclaimer_label.setAlignment(Qt.AlignHCenter)
        data_group_box_layout.addWidget(ccn_disclaimer_label)
        layout.addSpacing(10)

        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        layout.addWidget(self.button_box)

        self.resize(self.sizeHint())
Ejemplo n.º 26
0
    def __init__(self, parent):
        super(MatrixDialog, self).__init__(parent)
        self.setWindowTitle(_("Trezor Matrix Recovery"))
        self.num = 9
        self.loop = QEventLoop()

        vbox = QVBoxLayout(self)
        vbox.addWidget(WWLabel(MATRIX_RECOVERY))

        grid = QGridLayout()
        grid.setSpacing(0)
        self.char_buttons = []
        for y in range(3):
            for x in range(3):
                button = QPushButton('?')
                button.clicked.connect(partial(self.process_key, ord('1') + y * 3 + x))
                grid.addWidget(button, 3 - y, x)
                self.char_buttons.append(button)
        vbox.addLayout(grid)

        self.backspace_button = QPushButton("<=")
        self.backspace_button.clicked.connect(partial(self.process_key, Qt.Key_Backspace))
        self.cancel_button = QPushButton(_("Cancel"))
        self.cancel_button.clicked.connect(partial(self.process_key, Qt.Key_Escape))
        buttons = Buttons(self.backspace_button, self.cancel_button)
        vbox.addSpacing(40)
        vbox.addLayout(buttons)
        self.refresh()
        self.show()
Ejemplo n.º 27
0
class CustomColumnsTab(QWidget):

    def __init__(self, parent_dialog, plugin_action):
        self.parent_dialog = parent_dialog
        self.plugin_action = plugin_action
        QWidget.__init__(self)
        
        self.l = QVBoxLayout()
        self.setLayout(self.l)

        label = QLabel(_("If you have custom columns defined, they will be listed below.  Choose how you would like these columns handled."))
        label.setWordWrap(True)
        self.l.addWidget(label)
        self.l.addSpacing(5)
        
        scrollable = QScrollArea()
        scrollcontent = QWidget()
        scrollable.setWidget(scrollcontent)
        scrollable.setWidgetResizable(True)
        self.l.addWidget(scrollable)

        self.sl = QVBoxLayout()
        scrollcontent.setLayout(self.sl)
        
        self.custcol_dropdowns = {}

        custom_columns = self.plugin_action.gui.library_view.model().custom_columns

        grid = QGridLayout()
        self.sl.addLayout(grid)
        row=0
        for key, column in custom_columns.iteritems():
            if column['datatype'] in permitted_values:
                # print("\n============== %s ===========\n"%key)
                # for (k,v) in column.iteritems():
                #     print("column['%s'] => %s"%(k,v))
                label = QLabel('%s(%s)'%(column['name'],key))
                label.setToolTip(_("Set this %s column on new merged books...")%column['datatype'])
                grid.addWidget(label,row,0)

                dropdown = QComboBox(self)
                dropdown.addItem('','none')
                for md in permitted_values[column['datatype']]:
                    # tags-like column also 'text'
                    if md == 'union' and not column['is_multiple']:
                        continue
                    if md == 'concat' and column['is_multiple']:
                        continue
                    dropdown.addItem(titleLabels[md],md)
                self.custcol_dropdowns[key] = dropdown
                if key in prefs['custom_cols']:
                    dropdown.setCurrentIndex(dropdown.findData(prefs['custom_cols'][key]))
                dropdown.setToolTip(_("How this column will be populated by default."))
                grid.addWidget(dropdown,row,1)
                row+=1
        
        self.sl.insertStretch(-1)
Ejemplo n.º 28
0
    def __init__(self, gui, interface_action, books):
        '''
        :param gui: Parent gui
        :param interface_action: InterfaceActionObject (InterfacePluginAction class from action.py)
        :param books: list of Kobo book
        '''
        
        self.books = books
        self.gui = gui
        self.interface_action = interface_action
        self.books = books

        SizePersistedDialog.__init__(self, gui, PLUGIN_NAME + 'plugin:selections dialog')
        self.setWindowTitle(_(PLUGIN_NAME + ' v' + PLUGIN_VERSION))
        self.setMinimumWidth(300)
        self.setMinimumHeight(300)
        layout = QVBoxLayout(self)
        self.setLayout(layout)
        title_layout = ImageTitleLayout(self, 'images/obok.png', _('Obok DeDRM'))
        layout.addLayout(title_layout)

        help_label = QLabel(_('<a href="http://www.foo.com/">Help</a>'), self)
        help_label.setTextInteractionFlags(Qt.LinksAccessibleByMouse | Qt.LinksAccessibleByKeyboard)
        help_label.setAlignment(Qt.AlignRight)
        help_label.linkActivated.connect(self._help_link_activated)
        title_layout.addWidget(help_label)
        title_layout.setAlignment(Qt.AlignTop)

        layout.addSpacing(5)
        main_layout = QHBoxLayout()
        layout.addLayout(main_layout)
#        self.listy = QListWidget()
#        self.listy.setSelectionMode(QAbstractItemView.ExtendedSelection)
#        main_layout.addWidget(self.listy)
#        self.listy.addItems(books)
        self.books_table = BookListTableWidget(self)
        main_layout.addWidget(self.books_table)

        layout.addSpacing(10)
        button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        button_box.accepted.connect(self._ok_clicked)
        button_box.rejected.connect(self.reject)
        self.select_all_button = button_box.addButton(_("Select All"), QDialogButtonBox.ResetRole)
        self.select_all_button.setToolTip(_("Select all books to add them to the calibre library."))
        self.select_all_button.clicked.connect(self._select_all_clicked)
        self.select_drm_button = button_box.addButton(_("All with DRM"), QDialogButtonBox.ResetRole)
        self.select_drm_button.setToolTip(_("Select all books with DRM."))
        self.select_drm_button.clicked.connect(self._select_drm_clicked)
        self.select_free_button = button_box.addButton(_("All DRM free"), QDialogButtonBox.ResetRole)
        self.select_free_button.setToolTip(_("Select all books without DRM."))
        self.select_free_button.clicked.connect(self._select_free_clicked)
        layout.addWidget(button_box)

        # Cause our dialog size to be restored from prefs or created on first usage
        self.resize_dialog()
        self.books_table.populate_table(self.books)
Ejemplo n.º 29
0
class TabExtendedConfig(DeviceConfigTab):
    def __init__(self, parent, device):
        super(TabExtendedConfig, self).__init__(parent)

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

        self.extended_options = ExtendedGroupBox(self, device)
        self.l.addWidget(self.extended_options)
        self.addDeviceWidget(self.extended_options)
Ejemplo n.º 30
0
    def __init__(self, info_str, parent=None):
        super(InfoDialog, self).__init__(parent)

        layout = QVBoxLayout(self)
        layout.addWidget(QLabel(info_str))

        buttons = QDialogButtonBox(QDialogButtonBox.Ok, Qt.Horizontal, self)
        buttons.accepted.connect(self.accept)

        layout.addWidget(buttons)
Ejemplo n.º 31
0
class SearchTheInternet(QWidget):

    changed_signal = pyqtSignal()

    def __init__(self, parent):
        QWidget.__init__(self, parent)
        self.sa = QScrollArea(self)
        self.lw = QWidget(self)
        self.l = QVBoxLayout(self.lw)
        self.sa.setWidget(self.lw), self.sa.setWidgetResizable(True)
        self.gl = gl = QVBoxLayout(self)
        self.la = QLabel(_(
            'Add new locations to search for books or authors using the "Search the internet" feature'
            ' of the Content server. The URLs should contain {author} which will be'
            ' replaced by the author name and, for book URLs, {title} which will'
            ' be replaced by the book title.'))
        self.la.setWordWrap(True)
        gl.addWidget(self.la)

        self.h = QHBoxLayout()
        gl.addLayout(self.h)
        self.add_url_button = b = QPushButton(QIcon(I('plus.png')), _('&Add URL'))
        b.clicked.connect(self.add_url)
        self.h.addWidget(b)
        self.export_button = b = QPushButton(_('Export URLs'))
        b.clicked.connect(self.export_urls)
        self.h.addWidget(b)
        self.import_button = b = QPushButton(_('Import URLs'))
        b.clicked.connect(self.import_urls)
        self.h.addWidget(b)
        self.clear_button = b = QPushButton(_('Clear'))
        b.clicked.connect(self.clear)
        self.h.addWidget(b)

        self.h.addStretch(10)
        gl.addWidget(self.sa, stretch=10)
        self.items = []

    def genesis(self):
        self.current_urls = search_the_net_urls() or []

    @property
    def current_urls(self):
        return [item.as_dict for item in self.items if not item.is_empty]

    def append_item(self, item_as_dict):
        self.items.append(URLItem(item_as_dict, self))
        self.l.addWidget(self.items[-1])

    def clear(self):
        [(self.l.removeWidget(w), w.setParent(None), w.deleteLater()) for w in self.items]
        self.items = []
        self.changed_signal.emit()

    @current_urls.setter
    def current_urls(self, val):
        self.clear()
        for entry in val:
            self.append_item(entry)

    def add_url(self):
        self.items.append(URLItem(None, self))
        self.l.addWidget(self.items[-1])
        QTimer.singleShot(100, self.scroll_to_bottom)

    def scroll_to_bottom(self):
        sb = self.sa.verticalScrollBar()
        if sb:
            sb.setValue(sb.maximum())
        self.items[-1].name_widget.setFocus(Qt.FocusReason.OtherFocusReason)

    @property
    def serialized_urls(self):
        return json.dumps(self.current_urls, indent=2)

    def commit(self):
        for item in self.items:
            if not item.validate():
                return False
        cu = self.current_urls
        if cu:
            with lopen(search_the_net_urls.path, 'wb') as f:
                f.write(self.serialized_urls)
        else:
            try:
                os.remove(search_the_net_urls.path)
            except EnvironmentError as err:
                if err.errno != errno.ENOENT:
                    raise
        return True

    def export_urls(self):
        path = choose_save_file(
            self, 'search-net-urls', _('Choose URLs file'),
            filters=[(_('URL files'), ['json'])], initial_filename='search-urls.json')
        if path:
            with lopen(path, 'wb') as f:
                f.write(self.serialized_urls)

    def import_urls(self):
        paths = choose_files(self, 'search-net-urls', _('Choose URLs file'),
            filters=[(_('URL files'), ['json'])], all_files=False, select_only_single_file=True)
        if paths:
            with lopen(paths[0], 'rb') as f:
                items = json.loads(f.read())
                [self.append_item(x) for x in items]
                self.changed_signal.emit()
Ejemplo n.º 32
0
    def _setup_ui(self):
        layout = QVBoxLayout()

        layout.addWidget(self._label)

        self.setLayout(layout)
Ejemplo n.º 33
0
    def __init__(self, window, plugin, keystore, device_id):
        title = _("{} Settings").format(plugin.device)
        super(SettingsDialog, self).__init__(window, title)
        self.setMaximumWidth(540)

        devmgr = plugin.device_manager()
        config = devmgr.config
        handler = keystore.handler
        thread = keystore.thread
        hs_rows, hs_cols = (64, 128)

        def invoke_client(method, *args, **kw_args):
            unpair_after = kw_args.pop('unpair_after', False)

            def task():
                client = devmgr.client_by_id(device_id)
                if not client:
                    raise RuntimeError("Device not connected")
                if method:
                    getattr(client, method)(*args, **kw_args)
                if unpair_after:
                    devmgr.unpair_id(device_id)
                return client.features

            thread.add(task, on_success=update)

        def update(features):
            self.features = features
            set_label_enabled()
            if features.bootloader_hash:
                bl_hash = bh2u(features.bootloader_hash)
                bl_hash = "\n".join([bl_hash[:32], bl_hash[32:]])
            else:
                bl_hash = "N/A"
            noyes = [_("No"), _("Yes")]
            endis = [_("Enable Passphrases"), _("Disable Passphrases")]
            disen = [_("Disabled"), _("Enabled")]
            setchange = [_("Set a PIN"), _("Change PIN")]

            version = "%d.%d.%d" % (features.major_version,
                                    features.minor_version,
                                    features.patch_version)

            device_label.setText(features.label)
            pin_set_label.setText(noyes[features.pin_protection])
            passphrases_label.setText(disen[features.passphrase_protection])
            bl_hash_label.setText(bl_hash)
            label_edit.setText(features.label)
            device_id_label.setText(features.device_id)
            initialized_label.setText(noyes[features.initialized])
            version_label.setText(version)
            clear_pin_button.setVisible(features.pin_protection)
            clear_pin_warning.setVisible(features.pin_protection)
            pin_button.setText(setchange[features.pin_protection])
            pin_msg.setVisible(not features.pin_protection)
            passphrase_button.setText(endis[features.passphrase_protection])
            language_label.setText(features.language)

        def set_label_enabled():
            label_apply.setEnabled(label_edit.text() != self.features.label)

        def rename():
            invoke_client('change_label', label_edit.text())

        def toggle_passphrase():
            title = _("Confirm Toggle Passphrase Protection")
            currently_enabled = self.features.passphrase_protection
            if currently_enabled:
                msg = _("After disabling passphrases, you can only pair this "
                        "Electrum-NMC wallet if it had an empty passphrase.  "
                        "If its passphrase was not empty, you will need to "
                        "create a new wallet with the install wizard.  You "
                        "can use this wallet again at any time by re-enabling "
                        "passphrases and entering its passphrase.")
            else:
                msg = _(
                    "Your current Electrum-NMC wallet can only be used with "
                    "an empty passphrase.  You must create a separate "
                    "wallet with the install wizard for other passphrases "
                    "as each one generates a new set of addresses.")
            msg += "\n\n" + _("Are you sure you want to proceed?")
            if not self.question(msg, title=title):
                return
            invoke_client('toggle_passphrase', unpair_after=currently_enabled)

        def change_homescreen():
            dialog = QFileDialog(self, _("Choose Homescreen"))
            filename, __ = dialog.getOpenFileName()
            if not filename:
                return  # user cancelled

            if filename.endswith('.toif'):
                img = open(filename, 'rb').read()
                if img[:8] != b'TOIf\x90\x00\x90\x00':
                    handler.show_error(
                        'File is not a TOIF file with size of 144x144')
                    return
            else:
                from PIL import Image  # FIXME
                im = Image.open(filename)
                if im.size != (128, 64):
                    handler.show_error('Image must be 128 x 64 pixels')
                    return
                im = im.convert('1')
                pix = im.load()
                img = bytearray(1024)
                for j in range(64):
                    for i in range(128):
                        if pix[i, j]:
                            o = (i + j * 128)
                            img[o // 8] |= (1 << (7 - o % 8))
                img = bytes(img)
            invoke_client('change_homescreen', img)

        def clear_homescreen():
            invoke_client('change_homescreen', b'\x00')

        def set_pin():
            invoke_client('set_pin', remove=False)

        def clear_pin():
            invoke_client('set_pin', remove=True)

        def wipe_device():
            wallet = window.wallet
            if wallet and sum(wallet.get_balance()):
                title = _("Confirm Device Wipe")
                msg = _("Are you SURE you want to wipe the device?\n"
                        "Your wallet still has namecoins in it!")
                if not self.question(
                        msg, title=title, icon=QMessageBox.Critical):
                    return
            invoke_client('wipe_device', unpair_after=True)

        def slider_moved():
            mins = timeout_slider.sliderPosition()
            timeout_minutes.setText(_("%2d minutes") % mins)

        def slider_released():
            config.set_session_timeout(timeout_slider.sliderPosition() * 60)

        # Information tab
        info_tab = QWidget()
        info_layout = QVBoxLayout(info_tab)
        info_glayout = QGridLayout()
        info_glayout.setColumnStretch(2, 1)
        device_label = QLabel()
        pin_set_label = QLabel()
        passphrases_label = QLabel()
        version_label = QLabel()
        device_id_label = QLabel()
        bl_hash_label = QLabel()
        bl_hash_label.setWordWrap(True)
        language_label = QLabel()
        initialized_label = QLabel()
        rows = [
            (_("Device Label"), device_label),
            (_("PIN set"), pin_set_label),
            (_("Passphrases"), passphrases_label),
            (_("Firmware Version"), version_label),
            (_("Device ID"), device_id_label),
            (_("Bootloader Hash"), bl_hash_label),
            (_("Language"), language_label),
            (_("Initialized"), initialized_label),
        ]
        for row_num, (label, widget) in enumerate(rows):
            info_glayout.addWidget(QLabel(label), row_num, 0)
            info_glayout.addWidget(widget, row_num, 1)
        info_layout.addLayout(info_glayout)

        # Settings tab
        settings_tab = QWidget()
        settings_layout = QVBoxLayout(settings_tab)
        settings_glayout = QGridLayout()

        # Settings tab - Label
        label_msg = QLabel(
            _("Name this {}.  If you have multiple devices "
              "their labels help distinguish them.").format(plugin.device))
        label_msg.setWordWrap(True)
        label_label = QLabel(_("Device Label"))
        label_edit = QLineEdit()
        label_edit.setMinimumWidth(150)
        label_edit.setMaxLength(plugin.MAX_LABEL_LEN)
        label_apply = QPushButton(_("Apply"))
        label_apply.clicked.connect(rename)
        label_edit.textChanged.connect(set_label_enabled)
        settings_glayout.addWidget(label_label, 0, 0)
        settings_glayout.addWidget(label_edit, 0, 1, 1, 2)
        settings_glayout.addWidget(label_apply, 0, 3)
        settings_glayout.addWidget(label_msg, 1, 1, 1, -1)

        # Settings tab - PIN
        pin_label = QLabel(_("PIN Protection"))
        pin_button = QPushButton()
        pin_button.clicked.connect(set_pin)
        settings_glayout.addWidget(pin_label, 2, 0)
        settings_glayout.addWidget(pin_button, 2, 1)
        pin_msg = QLabel(
            _("PIN protection is strongly recommended.  "
              "A PIN is your only protection against someone "
              "stealing your namecoins if they obtain physical "
              "access to your {}.").format(plugin.device))
        pin_msg.setWordWrap(True)
        pin_msg.setStyleSheet("color: red")
        settings_glayout.addWidget(pin_msg, 3, 1, 1, -1)

        # Settings tab - Homescreen
        homescreen_label = QLabel(_("Homescreen"))
        homescreen_change_button = QPushButton(_("Change..."))
        homescreen_clear_button = QPushButton(_("Reset"))
        homescreen_change_button.clicked.connect(change_homescreen)
        try:
            import PIL
        except ImportError:
            homescreen_change_button.setDisabled(True)
            homescreen_change_button.setToolTip(
                _("Required package 'PIL' is not available - Please install it or use the Trezor website instead."
                  ))
        homescreen_clear_button.clicked.connect(clear_homescreen)
        homescreen_msg = QLabel(
            _("You can set the homescreen on your "
              "device to personalize it.  You must "
              "choose a {} x {} monochrome black and "
              "white image.").format(hs_rows, hs_cols))
        homescreen_msg.setWordWrap(True)
        settings_glayout.addWidget(homescreen_label, 4, 0)
        settings_glayout.addWidget(homescreen_change_button, 4, 1)
        settings_glayout.addWidget(homescreen_clear_button, 4, 2)
        settings_glayout.addWidget(homescreen_msg, 5, 1, 1, -1)

        # Settings tab - Session Timeout
        timeout_label = QLabel(_("Session Timeout"))
        timeout_minutes = QLabel()
        timeout_slider = QSlider(Qt.Horizontal)
        timeout_slider.setRange(1, 60)
        timeout_slider.setSingleStep(1)
        timeout_slider.setTickInterval(5)
        timeout_slider.setTickPosition(QSlider.TicksBelow)
        timeout_slider.setTracking(True)
        timeout_msg = QLabel(
            _("Clear the session after the specified period "
              "of inactivity.  Once a session has timed out, "
              "your PIN and passphrase (if enabled) must be "
              "re-entered to use the device."))
        timeout_msg.setWordWrap(True)
        timeout_slider.setSliderPosition(config.get_session_timeout() // 60)
        slider_moved()
        timeout_slider.valueChanged.connect(slider_moved)
        timeout_slider.sliderReleased.connect(slider_released)
        settings_glayout.addWidget(timeout_label, 6, 0)
        settings_glayout.addWidget(timeout_slider, 6, 1, 1, 3)
        settings_glayout.addWidget(timeout_minutes, 6, 4)
        settings_glayout.addWidget(timeout_msg, 7, 1, 1, -1)
        settings_layout.addLayout(settings_glayout)
        settings_layout.addStretch(1)

        # Advanced tab
        advanced_tab = QWidget()
        advanced_layout = QVBoxLayout(advanced_tab)
        advanced_glayout = QGridLayout()

        # Advanced tab - clear PIN
        clear_pin_button = QPushButton(_("Disable PIN"))
        clear_pin_button.clicked.connect(clear_pin)
        clear_pin_warning = QLabel(
            _("If you disable your PIN, anyone with physical access to your "
              "{} device can spend your namecoins.").format(plugin.device))
        clear_pin_warning.setWordWrap(True)
        clear_pin_warning.setStyleSheet("color: red")
        advanced_glayout.addWidget(clear_pin_button, 0, 2)
        advanced_glayout.addWidget(clear_pin_warning, 1, 0, 1, 5)

        # Advanced tab - toggle passphrase protection
        passphrase_button = QPushButton()
        passphrase_button.clicked.connect(toggle_passphrase)
        passphrase_msg = WWLabel(PASSPHRASE_HELP)
        passphrase_warning = WWLabel(PASSPHRASE_NOT_PIN)
        passphrase_warning.setStyleSheet("color: red")
        advanced_glayout.addWidget(passphrase_button, 3, 2)
        advanced_glayout.addWidget(passphrase_msg, 4, 0, 1, 5)
        advanced_glayout.addWidget(passphrase_warning, 5, 0, 1, 5)

        # Advanced tab - wipe device
        wipe_device_button = QPushButton(_("Wipe Device"))
        wipe_device_button.clicked.connect(wipe_device)
        wipe_device_msg = QLabel(
            _("Wipe the device, removing all data from it.  The firmware "
              "is left unchanged."))
        wipe_device_msg.setWordWrap(True)
        wipe_device_warning = QLabel(
            _("Only wipe a device if you have the recovery seed written down "
              "and the device wallet(s) are empty, otherwise the namecoins "
              "will be lost forever."))
        wipe_device_warning.setWordWrap(True)
        wipe_device_warning.setStyleSheet("color: red")
        advanced_glayout.addWidget(wipe_device_button, 6, 2)
        advanced_glayout.addWidget(wipe_device_msg, 7, 0, 1, 5)
        advanced_glayout.addWidget(wipe_device_warning, 8, 0, 1, 5)
        advanced_layout.addLayout(advanced_glayout)
        advanced_layout.addStretch(1)

        tabs = QTabWidget(self)
        tabs.addTab(info_tab, _("Information"))
        tabs.addTab(settings_tab, _("Settings"))
        tabs.addTab(advanced_tab, _("Advanced"))
        dialog_vbox = QVBoxLayout(self)
        dialog_vbox.addWidget(tabs)
        dialog_vbox.addLayout(Buttons(CloseButton(self)))

        # Update information
        invoke_client(None)
Ejemplo n.º 34
0
    def do_layout(self):
        self.central_widget.clear()
        self.labels = []
        sto = QWidget.setTabOrder

        self.central_widget.tabBar().setVisible(False)
        tab0 = QWidget(self)
        self.central_widget.addTab(tab0, _("&Metadata"))
        l = QGridLayout()
        tab0.setLayout(l)

        # Basic metadata in col 0
        tl = QGridLayout()
        gb = QGroupBox(_('Basic metadata'), tab0)
        l.addWidget(gb, 0, 0, 1, 1)
        gb.setLayout(tl)

        self.button_box_layout.insertWidget(1, self.fetch_metadata_button)
        self.button_box_layout.insertWidget(2, self.config_metadata_button)
        sto(self.button_box, self.fetch_metadata_button)
        sto(self.fetch_metadata_button, self.config_metadata_button)
        sto(self.config_metadata_button, self.title)

        def create_row(row, widget, tab_to, button=None, icon=None, span=1):
            ql = BuddyLabel(widget)
            tl.addWidget(ql, row, 1, 1, 1)
            tl.addWidget(widget, row, 2, 1, 1)
            if button is not None:
                tl.addWidget(button, row, 3, span, 1)
                if icon is not None:
                    button.setIcon(QIcon(I(icon)))
            if tab_to is not None:
                if button is not None:
                    sto(widget, button)
                    sto(button, tab_to)
                else:
                    sto(widget, tab_to)

        tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1)
        tl.addWidget(self.manage_authors_button, 2, 0, 2, 1)
        tl.addWidget(self.paste_isbn_button, 12, 0, 1, 1)
        tl.addWidget(self.tags_editor_button, 6, 0, 1, 1)

        create_row(0, self.title, self.title_sort,
                   button=self.deduce_title_sort_button, span=2,
                   icon='auto_author_sort.png')
        create_row(1, self.title_sort, self.authors)
        create_row(2, self.authors, self.author_sort,
                   button=self.deduce_author_sort_button,
                   span=2, icon='auto_author_sort.png')
        create_row(3, self.author_sort, self.series)
        create_row(4, self.series, self.series_index,
                   button=self.clear_series_button, icon='trash.png')
        create_row(5, self.series_index, self.tags)
        create_row(6, self.tags, self.rating, button=self.clear_tags_button)
        create_row(7, self.rating, self.pubdate, button=self.clear_ratings_button)
        create_row(8, self.pubdate, self.publisher,
                   button=self.pubdate.clear_button, icon='trash.png')
        create_row(9, self.publisher, self.languages,
                   button=self.publisher.clear_button, icon='trash.png')
        create_row(10, self.languages, self.timestamp)
        create_row(11, self.timestamp, self.identifiers,
                   button=self.timestamp.clear_button, icon='trash.png')
        create_row(12, self.identifiers, self.comments,
                   button=self.clear_identifiers_button, icon='trash.png')
        sto(self.clear_identifiers_button, self.swap_title_author_button)
        sto(self.swap_title_author_button, self.manage_authors_button)
        sto(self.manage_authors_button, self.tags_editor_button)
        sto(self.tags_editor_button, self.paste_isbn_button)
        tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding),
                   13, 1, 1 ,1)

        # Custom metadata in col 1
        w = getattr(self, 'custom_metadata_widgets_parent', None)
        if w is not None:
            gb = QGroupBox(_('Custom metadata'), tab0)
            gbl = QVBoxLayout()
            gb.setLayout(gbl)
            sr = QScrollArea(gb)
            sr.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            sr.setWidgetResizable(True)
            sr.setFrameStyle(QFrame.NoFrame)
            sr.setWidget(w)
            gbl.addWidget(sr)
            l.addWidget(gb, 0, 1, 1, 1)
            sp = QSizePolicy()
            sp.setVerticalStretch(10)
            sp.setHorizontalPolicy(QSizePolicy.Minimum)
            sp.setVerticalPolicy(QSizePolicy.Expanding)
            gb.setSizePolicy(sp)
            self.set_custom_metadata_tab_order()

        # comments span col 0 & 1
        w = QGroupBox(_('Comments'), tab0)
        sp = QSizePolicy()
        sp.setVerticalStretch(10)
        sp.setHorizontalPolicy(QSizePolicy.Expanding)
        sp.setVerticalPolicy(QSizePolicy.Expanding)
        w.setSizePolicy(sp)
        lb = QHBoxLayout()
        w.setLayout(lb)
        lb.addWidget(self.comments)
        l.addWidget(w, 1, 0, 1, 2)

        # Cover & formats in col 3
        gb = QGroupBox(_('Cover'), tab0)
        lb = QGridLayout()
        gb.setLayout(lb)
        lb.addWidget(self.cover, 0, 0, 1, 3, alignment=Qt.AlignCenter)
        sto(self.manage_authors_button, self.cover.buttons[0])
        for i, b in enumerate(self.cover.buttons[:3]):
            lb.addWidget(b, 1, i, 1, 1)
            sto(b, self.cover.buttons[i+1])
        hl = QHBoxLayout()
        for b in self.cover.buttons[3:]:
            hl.addWidget(b)
        sto(self.cover.buttons[-2], self.cover.buttons[-1])
        lb.addLayout(hl, 2, 0, 1, 3)
        l.addWidget(gb, 0, 2, 1, 1)
        l.addWidget(self.formats_manager, 1, 2, 1, 1)
        sto(self.cover.buttons[-1], self.formats_manager)

        self.formats_manager.formats.setMaximumWidth(10000)
        self.formats_manager.formats.setIconSize(QSize(32, 32))
Ejemplo n.º 35
0
class MetadataSingleDialogBase(ResizableDialog):

    view_format = pyqtSignal(object, object)
    cc_two_column = tweaks['metadata_single_use_2_cols_for_custom_fields']
    one_line_comments_toolbar = False
    use_toolbutton_for_config_metadata = True

    def __init__(self, db, parent=None, editing_multiple=False):
        self.db = db
        self.changed = set()
        self.books_to_refresh = set()
        self.rows_to_refresh = set()
        self.metadata_before_fetch = None
        self.editing_multiple = editing_multiple
        ResizableDialog.__init__(self, parent)

    def setupUi(self, *args):  # {{{
        self.resize(990, 670)

        self.download_shortcut = QShortcut(self)
        self.download_shortcut.setKey(QKeySequence('Ctrl+D',
            QKeySequence.PortableText))
        p = self.parent()
        if hasattr(p, 'keyboard'):
            kname = u'Interface Action: Edit Metadata (Edit Metadata) : menu action : download'
            sc = p.keyboard.keys_map.get(kname, None)
            if sc:
                self.download_shortcut.setKey(sc[0])
        self.swap_title_author_shortcut = s = QShortcut(self)
        s.setKey(QKeySequence('Alt+Down', QKeySequence.PortableText))

        self.button_box = bb = QDialogButtonBox(self)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        self.next_button = QPushButton(QIcon(I('forward.png')), _('Next'),
                self)
        self.next_button.setShortcut(QKeySequence('Alt+Right'))
        self.next_button.clicked.connect(self.next_clicked)
        self.prev_button = QPushButton(QIcon(I('back.png')), _('Previous'),
                self)
        self.prev_button.setShortcut(QKeySequence('Alt+Left'))

        self.button_box.addButton(self.prev_button, bb.ActionRole)
        self.button_box.addButton(self.next_button, bb.ActionRole)
        self.prev_button.clicked.connect(self.prev_clicked)
        bb.setStandardButtons(bb.Ok|bb.Cancel)
        bb.button(bb.Ok).setDefault(True)

        self.scroll_area = QScrollArea(self)
        self.scroll_area.setFrameShape(QScrollArea.NoFrame)
        self.scroll_area.setWidgetResizable(True)
        self.central_widget = QTabWidget(self)
        self.scroll_area.setWidget(self.central_widget)

        self.l = QVBoxLayout(self)
        self.setLayout(self.l)
        self.l.addWidget(self.scroll_area)
        ll = self.button_box_layout = QHBoxLayout()
        self.l.addLayout(ll)
        ll.addSpacing(10)
        ll.addWidget(self.button_box)

        self.setWindowIcon(QIcon(I('edit_input.png')))
        self.setWindowTitle(BASE_TITLE)

        self.create_basic_metadata_widgets()

        if len(self.db.custom_column_label_map):
            self.create_custom_metadata_widgets()

        self.do_layout()
        geom = gprefs.get('metasingle_window_geometry3', None)
        if geom is not None:
            self.restoreGeometry(bytes(geom))
    # }}}

    def create_basic_metadata_widgets(self):  # {{{
        self.basic_metadata_widgets = []

        self.languages = LanguagesEdit(self)
        self.basic_metadata_widgets.append(self.languages)

        self.title = TitleEdit(self)
        self.title.textChanged.connect(self.update_window_title)
        self.deduce_title_sort_button = QToolButton(self)
        self.deduce_title_sort_button.setToolTip(
            _('Automatically create the title sort entry based on the current '
                'title entry.\nUsing this button to create title sort will '
                'change title sort from red to green.'))
        self.deduce_title_sort_button.setWhatsThis(
                self.deduce_title_sort_button.toolTip())
        self.title_sort = TitleSortEdit(self, self.title,
                self.deduce_title_sort_button, self.languages)
        self.basic_metadata_widgets.extend([self.title, self.title_sort])

        self.deduce_author_sort_button = b = RightClickButton(self)
        b.setToolTip('<p>' +
            _('Automatically create the author sort entry based on the current '
              'author entry. Using this button to create author sort will '
              'change author sort from red to green.  There is a menu of '
              'functions available under this button. Click and hold '
              'on the button to see it.') + '</p>')
        if isosx:
            # Workaround for https://bugreports.qt-project.org/browse/QTBUG-41017
            class Menu(QMenu):

                def mouseReleaseEvent(self, ev):
                    ac = self.actionAt(ev.pos())
                    if ac is not None:
                        ac.trigger()
                    return QMenu.mouseReleaseEvent(self, ev)
            b.m = m = Menu()
        else:
            b.m = m = QMenu()
        ac = m.addAction(QIcon(I('forward.png')), _('Set author sort from author'))
        ac2 = m.addAction(QIcon(I('back.png')), _('Set author from author sort'))
        ac3 = m.addAction(QIcon(I('user_profile.png')), _('Manage authors'))
        ac4 = m.addAction(QIcon(I('next.png')),
                _('Copy author to author sort'))
        ac5 = m.addAction(QIcon(I('previous.png')),
                _('Copy author sort to author'))

        b.setMenu(m)
        self.authors = AuthorsEdit(self, ac3)
        self.author_sort = AuthorSortEdit(self, self.authors, b, self.db, ac,
                ac2, ac4, ac5)
        self.basic_metadata_widgets.extend([self.authors, self.author_sort])

        self.swap_title_author_button = QToolButton(self)
        self.swap_title_author_button.setIcon(QIcon(I('swap.png')))
        self.swap_title_author_button.setToolTip(_(
            'Swap the author and title') + ' [%s]' % self.swap_title_author_shortcut.key().toString(QKeySequence.NativeText))
        self.swap_title_author_button.clicked.connect(self.swap_title_author)
        self.swap_title_author_shortcut.activated.connect(self.swap_title_author_button.click)

        self.manage_authors_button = QToolButton(self)
        self.manage_authors_button.setIcon(QIcon(I('user_profile.png')))
        self.manage_authors_button.setToolTip('<p>' + _(
            'Manage authors. Use to rename authors and correct '
            'individual author\'s sort values') + '</p>')
        self.manage_authors_button.clicked.connect(self.authors.manage_authors)

        self.series = SeriesEdit(self)
        self.clear_series_button = QToolButton(self)
        self.clear_series_button.setToolTip(
               _('Clear series'))
        self.clear_series_button.clicked.connect(self.series.clear)
        self.series_index = SeriesIndexEdit(self, self.series)
        self.basic_metadata_widgets.extend([self.series, self.series_index])

        self.formats_manager = FormatsManager(self, self.copy_fmt)
        # We want formats changes to be committed before title/author, as
        # otherwise we could have data loss if the title/author changed and the
        # user was trying to add an extra file from the old books directory.
        self.basic_metadata_widgets.insert(0, self.formats_manager)
        self.formats_manager.metadata_from_format_button.clicked.connect(
                self.metadata_from_format)
        self.formats_manager.cover_from_format_button.clicked.connect(
                self.cover_from_format)
        self.cover = Cover(self)
        self.cover.download_cover.connect(self.download_cover)
        self.basic_metadata_widgets.append(self.cover)

        self.comments = CommentsEdit(self, self.one_line_comments_toolbar)
        self.basic_metadata_widgets.append(self.comments)

        self.rating = RatingEdit(self)
        self.clear_ratings_button = QToolButton(self)
        self.clear_ratings_button.setToolTip(_('Clear rating'))
        self.clear_ratings_button.setIcon(QIcon(I('trash.png')))
        self.clear_ratings_button.clicked.connect(self.rating.zero)

        self.basic_metadata_widgets.append(self.rating)

        self.tags = TagsEdit(self)
        self.tags_editor_button = QToolButton(self)
        self.tags_editor_button.setToolTip(_('Open Tag Editor'))
        self.tags_editor_button.setIcon(QIcon(I('chapters.png')))
        self.tags_editor_button.clicked.connect(self.tags_editor)
        self.clear_tags_button = QToolButton(self)
        self.clear_tags_button.setToolTip(_('Clear all tags'))
        self.clear_tags_button.setIcon(QIcon(I('trash.png')))
        self.clear_tags_button.clicked.connect(self.tags.clear)
        self.basic_metadata_widgets.append(self.tags)

        self.identifiers = IdentifiersEdit(self)
        self.basic_metadata_widgets.append(self.identifiers)
        self.clear_identifiers_button = QToolButton(self)
        self.clear_identifiers_button.setIcon(QIcon(I('trash.png')))
        self.clear_identifiers_button.setToolTip(_('Clear Ids'))
        self.clear_identifiers_button.clicked.connect(self.identifiers.clear)
        self.paste_isbn_button = QToolButton(self)
        self.paste_isbn_button.setToolTip('<p>' +
                    _('Paste the contents of the clipboard into the '
                      'identifiers box prefixed with isbn:') + '</p>')
        self.paste_isbn_button.setIcon(QIcon(I('edit-paste.png')))
        self.paste_isbn_button.clicked.connect(self.identifiers.paste_isbn)

        self.publisher = PublisherEdit(self)
        self.basic_metadata_widgets.append(self.publisher)

        self.timestamp = DateEdit(self)
        self.pubdate = PubdateEdit(self)
        self.basic_metadata_widgets.extend([self.timestamp, self.pubdate])

        self.fetch_metadata_button = b = RightClickButton(self)
        # The following rigmarole is needed so that Qt gives the button the
        # same height as the other buttons in the dialog. There is no way to
        # center the text in a QToolButton with an icon, so we cant just set an
        # icon
        b.setIcon(QIcon(I('download-metadata.png')))
        b.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        b.setMinimumHeight(b.sizeHint().height())
        b.setIcon(QIcon())
        b.setText(_('&Download metadata')), b.setPopupMode(b.DelayedPopup)
        b.setToolTip(_('Download metadata for this book [%s]') % self.download_shortcut.key().toString(QKeySequence.NativeText))
        b.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed))
        self.fetch_metadata_button.clicked.connect(self.fetch_metadata)
        self.fetch_metadata_menu = m = QMenu(self.fetch_metadata_button)
        m.addAction(QIcon(I('edit-undo.png')), _('Undo last metadata download'), self.undo_fetch_metadata)
        self.fetch_metadata_button.setMenu(m)
        self.download_shortcut.activated.connect(self.fetch_metadata_button.click)
        font = self.fmb_font = QFont()
        font.setBold(True)
        self.fetch_metadata_button.setFont(font)

        if self.use_toolbutton_for_config_metadata:
            self.config_metadata_button = QToolButton(self)
            self.config_metadata_button.setIcon(QIcon(I('config.png')))
        else:
            self.config_metadata_button = QPushButton(self)
            self.config_metadata_button.setText(_('Configure download metadata'))
        self.config_metadata_button.setIcon(QIcon(I('config.png')))
        self.config_metadata_button.clicked.connect(self.configure_metadata)
        self.config_metadata_button.setToolTip(
            _('Change how calibre downloads metadata'))

    # }}}

    def create_custom_metadata_widgets(self):  # {{{
        self.custom_metadata_widgets_parent = w = QWidget(self)
        layout = QGridLayout()
        w.setLayout(layout)
        self.custom_metadata_widgets, self.__cc_spacers = \
            populate_metadata_page(layout, self.db, None, parent=w, bulk=False,
                two_column=self.cc_two_column)
        self.__custom_col_layouts = [layout]
    # }}}

    def set_custom_metadata_tab_order(self, before=None, after=None):  # {{{
        sto = QWidget.setTabOrder
        if getattr(self, 'custom_metadata_widgets', []):
            ans = self.custom_metadata_widgets
            for i in range(len(ans)-1):
                if before is not None and i == 0:
                    pass
                if len(ans[i+1].widgets) == 2:
                    sto(ans[i].widgets[-1], ans[i+1].widgets[1])
                else:
                    sto(ans[i].widgets[-1], ans[i+1].widgets[0])
                for c in range(2, len(ans[i].widgets), 2):
                    sto(ans[i].widgets[c-1], ans[i].widgets[c+1])
            if after is not None:
                pass
    # }}}

    def do_view_format(self, path, fmt):
        if path:
            self.view_format.emit(None, path)
        else:
            self.view_format.emit(self.book_id, fmt)

    def copy_fmt(self, fmt, f):
        self.db.copy_format_to(self.book_id, fmt, f, index_is_id=True)

    def do_layout(self):
        raise NotImplementedError()

    def __call__(self, id_):
        self.book_id = id_
        self.books_to_refresh = set([])
        self.metadata_before_fetch = None
        for widget in self.basic_metadata_widgets:
            widget.initialize(self.db, id_)
        for widget in getattr(self, 'custom_metadata_widgets', []):
            widget.initialize(id_)
        if callable(self.set_current_callback):
            self.set_current_callback(id_)
        # Commented out as it doesn't play nice with Next, Prev buttons
        # self.fetch_metadata_button.setFocus(Qt.OtherFocusReason)

    # Miscellaneous interaction methods {{{
    def update_window_title(self, *args):
        title = self.title.current_val
        if len(title) > 50:
            title = title[:50] + u'\u2026'
        self.setWindowTitle(BASE_TITLE + ' - ' +
                title + ' - ' +
                _(' [%(num)d of %(tot)d]')%dict(num=self.current_row+1,
                tot=len(self.row_list)))

    def swap_title_author(self, *args):
        title = self.title.current_val
        self.title.current_val = authors_to_string(self.authors.current_val)
        self.authors.current_val = string_to_authors(title)
        self.title_sort.auto_generate()
        self.author_sort.auto_generate()

    def tags_editor(self, *args):
        self.tags.edit(self.db, self.book_id)

    def metadata_from_format(self, *args):
        mi, ext = self.formats_manager.get_selected_format_metadata(self.db,
                self.book_id)
        if mi is not None:
            self.update_from_mi(mi)

    def get_pdf_cover(self):
        pdfpath = self.formats_manager.get_format_path(self.db, self.book_id,
                                                       'pdf')
        from calibre.gui2.metadata.pdf_covers import PDFCovers
        d = PDFCovers(pdfpath, parent=self)
        if d.exec_() == d.Accepted:
            cpath = d.cover_path
            if cpath:
                with open(cpath, 'rb') as f:
                    self.update_cover(f.read(), 'PDF')
        d.cleanup()

    def cover_from_format(self, *args):
        ext = self.formats_manager.get_selected_format()
        if ext is None:
            return
        if ext == 'pdf':
            return self.get_pdf_cover()
        try:
            mi, ext = self.formats_manager.get_selected_format_metadata(self.db,
                    self.book_id)
        except (IOError, OSError) as err:
            if getattr(err, 'errno', None) == errno.EACCES:  # Permission denied
                import traceback
                fname = err.filename if err.filename else 'file'
                error_dialog(self, _('Permission denied'),
                        _('Could not open %s. Is it being used by another'
                        ' program?')%fname, det_msg=traceback.format_exc(),
                        show=True)
                return
            raise
        if mi is None:
            return
        cdata = None
        if mi.cover and os.access(mi.cover, os.R_OK):
            cdata = open(mi.cover).read()
        elif mi.cover_data[1] is not None:
            cdata = mi.cover_data[1]
        if cdata is None:
            error_dialog(self, _('Could not read cover'),
                         _('Could not read cover from %s format')%ext).exec_()
            return
        self.update_cover(cdata, ext)

    def update_cover(self, cdata, fmt):
        orig = self.cover.current_val
        self.cover.current_val = cdata
        if self.cover.current_val is None:
            self.cover.current_val = orig
            return error_dialog(self, _('Could not read cover'),
                         _('The cover in the %s format is invalid')%fmt,
                         show=True)
            return

    def update_from_mi(self, mi, update_sorts=True, merge_tags=True, merge_comments=False):
        fw = self.focusWidget()
        if not mi.is_null('title'):
            self.title.set_value(mi.title)
            if update_sorts:
                self.title_sort.auto_generate()
        if not mi.is_null('authors'):
            self.authors.set_value(mi.authors)
        if not mi.is_null('author_sort'):
            self.author_sort.set_value(mi.author_sort)
        elif update_sorts:
            self.author_sort.auto_generate()
        if not mi.is_null('rating'):
            try:
                self.rating.set_value(mi.rating)
            except:
                pass
        if not mi.is_null('publisher'):
            self.publisher.set_value(mi.publisher)
        if not mi.is_null('tags'):
            old_tags = self.tags.current_val
            tags = mi.tags if mi.tags else []
            if old_tags and merge_tags:
                ltags, lotags = {t.lower() for t in tags}, {t.lower() for t in
                        old_tags}
                tags = [t for t in tags if t.lower() in ltags-lotags] + old_tags
            self.tags.set_value(tags)
        if not mi.is_null('identifiers'):
            current = self.identifiers.current_val
            current.update(mi.identifiers)
            self.identifiers.set_value(current)
        if not mi.is_null('pubdate'):
            self.pubdate.set_value(mi.pubdate)
        if not mi.is_null('series') and mi.series.strip():
            self.series.set_value(mi.series)
            if mi.series_index is not None:
                self.series_index.reset_original()
                self.series_index.set_value(float(mi.series_index))
        if not mi.is_null('languages'):
            langs = [canonicalize_lang(x) for x in mi.languages]
            langs = [x for x in langs if x is not None]
            if langs:
                self.languages.set_value(langs)
        if mi.comments and mi.comments.strip():
            val = mi.comments
            if val and merge_comments:
                cval = self.comments.current_val
                if cval:
                    val = merge_two_comments(cval, val)
            self.comments.set_value(val)
        if fw is not None:
            fw.setFocus(Qt.OtherFocusReason)

    def fetch_metadata(self, *args):
        d = FullFetch(self.cover.pixmap(), self)
        ret = d.start(title=self.title.current_val, authors=self.authors.current_val,
                identifiers=self.identifiers.current_val)
        if ret == d.Accepted:
            self.metadata_before_fetch = {f:getattr(self, f).current_val for f in fetched_fields}
            from calibre.ebooks.metadata.sources.prefs import msprefs
            mi = d.book
            dummy = Metadata(_('Unknown'))
            for f in msprefs['ignore_fields']:
                if ':' not in f:
                    setattr(mi, f, getattr(dummy, f))
            if mi is not None:
                pd = mi.pubdate
                if pd is not None:
                    # Put the downloaded published date into the local timezone
                    # as we discard time info and the date is timezone
                    # invariant. This prevents the as_local_timezone() call in
                    # update_from_mi from changing the pubdate
                    mi.pubdate = datetime(pd.year, pd.month, pd.day,
                            tzinfo=local_tz)
                self.update_from_mi(mi, merge_comments=msprefs['append_comments'])
            if d.cover_pixmap is not None:
                self.metadata_before_fetch['cover'] = self.cover.current_val
                self.cover.current_val = pixmap_to_data(d.cover_pixmap)

    def undo_fetch_metadata(self):
        if self.metadata_before_fetch is None:
            return error_dialog(self, _('No downloaded metadata'), _(
                'There is no downloaded metadata to undo'), show=True)
        for field, val in self.metadata_before_fetch.iteritems():
            getattr(self, field).current_val = val
        self.metadata_before_fetch = None

    def configure_metadata(self):
        from calibre.gui2.preferences import show_config_widget
        gui = self.parent()
        show_config_widget('Sharing', 'Metadata download', parent=self,
                gui=gui, never_shutdown=True)

    def download_cover(self, *args):
        from calibre.gui2.metadata.single_download import CoverFetch
        d = CoverFetch(self.cover.pixmap(), self)
        ret = d.start(self.title.current_val, self.authors.current_val,
                self.identifiers.current_val)
        if ret == d.Accepted:
            if d.cover_pixmap is not None:
                self.cover.current_val = pixmap_to_data(d.cover_pixmap)

    # }}}

    def to_book_metadata(self):
        mi = Metadata(_('Unknown'))
        if self.db is None:
            return mi
        mi.set_all_user_metadata(self.db.field_metadata.custom_field_metadata())
        for widget in self.basic_metadata_widgets:
            widget.apply_to_metadata(mi)
        for widget in getattr(self, 'custom_metadata_widgets', []):
            widget.apply_to_metadata(mi)
        return mi

    def apply_changes(self):
        self.changed.add(self.book_id)
        if self.db is None:
            # break_cycles has already been called, don't know why this should
            # happen but a user reported it
            return True
        for widget in self.basic_metadata_widgets:
            try:
                if hasattr(widget, 'validate_for_commit'):
                    title, msg, det_msg = widget.validate_for_commit()
                    if title is not None:
                        error_dialog(self, title, msg, det_msg=det_msg, show=True)
                        return False
                widget.commit(self.db, self.book_id)
                self.books_to_refresh |= getattr(widget, 'books_to_refresh', set())
            except (IOError, OSError) as err:
                if getattr(err, 'errno', None) == errno.EACCES:  # Permission denied
                    import traceback
                    fname = getattr(err, 'filename', None)
                    p = 'Locked file: %s\n\n'%fname if fname else ''
                    error_dialog(self, _('Permission denied'),
                            _('Could not change the on disk location of this'
                                ' book. Is it open in another program?'),
                            det_msg=p+traceback.format_exc(), show=True)
                    return False
                raise
        for widget in getattr(self, 'custom_metadata_widgets', []):
            self.books_to_refresh |= widget.commit(self.book_id)

        self.db.commit()
        rows = self.db.refresh_ids(list(self.books_to_refresh))
        if rows:
            self.rows_to_refresh |= set(rows)

        return True

    def accept(self):
        self.save_state()
        if not self.apply_changes():
            return
        if self.editing_multiple and self.current_row != len(self.row_list) - 1:
            num = len(self.row_list) - 1 - self.current_row
            from calibre.gui2 import question_dialog
            if not question_dialog(
                    self, _('Are you sure?'),
                    _('There are still %d more books to edit in this set.'
                      ' Are you sure you want to stop? Use the Next button'
                      ' instead of the OK button to move through books in the set.') % num,
                    yes_text=_('&Stop editing'), no_text=_('&Continue editing'),
                    yes_icon='dot_red.png', no_icon='dot_green.png',
                    default_yes=False, skip_dialog_name='edit-metadata-single-confirm-ok-on-multiple'):
                return self.do_one(delta=1, apply_changes=False)
        ResizableDialog.accept(self)

    def reject(self):
        self.save_state()
        ResizableDialog.reject(self)

    def save_state(self):
        try:
            gprefs['metasingle_window_geometry3'] = bytearray(self.saveGeometry())
        except:
            # Weird failure, see https://bugs.launchpad.net/bugs/995271
            import traceback
            traceback.print_exc()

    # Dialog use methods {{{
    def start(self, row_list, current_row, view_slot=None,
            set_current_callback=None):
        self.row_list = row_list
        self.current_row = current_row
        if view_slot is not None:
            self.view_format.connect(view_slot)
        self.set_current_callback = set_current_callback
        self.do_one(apply_changes=False)
        ret = self.exec_()
        self.break_cycles()
        return ret

    def next_clicked(self):
        if not self.apply_changes():
            return
        self.do_one(delta=1, apply_changes=False)

    def prev_clicked(self):
        if not self.apply_changes():
            return
        self.do_one(delta=-1, apply_changes=False)

    def do_one(self, delta=0, apply_changes=True):
        if apply_changes:
            self.apply_changes()
        self.current_row += delta
        self.update_window_title()
        prev = next_ = None
        if self.current_row > 0:
            prev = self.db.title(self.row_list[self.current_row-1])
        if self.current_row < len(self.row_list) - 1:
            next_ = self.db.title(self.row_list[self.current_row+1])

        if next_ is not None:
            tip = (_('Save changes and edit the metadata of %s')+
                    ' [Alt+Right]')%next_
            self.next_button.setToolTip(tip)
        self.next_button.setEnabled(next_ is not None)
        if prev is not None:
            tip = (_('Save changes and edit the metadata of %s')+
                    ' [Alt+Left]')%prev
            self.prev_button.setToolTip(tip)
        self.prev_button.setEnabled(prev is not None)
        self.button_box.button(self.button_box.Ok).setDefault(True)
        self.button_box.button(self.button_box.Ok).setFocus(Qt.OtherFocusReason)
        self(self.db.id(self.row_list[self.current_row]))

    def break_cycles(self):
        # Break any reference cycles that could prevent python
        # from garbage collecting this dialog
        self.set_current_callback = self.db = None
        self.metadata_before_fetch = None
        def disconnect(signal):
            try:
                signal.disconnect()
            except:
                pass  # Fails if view format was never connected
        disconnect(self.view_format)
        for b in ('next_button', 'prev_button'):
            x = getattr(self, b, None)
            if x is not None:
                disconnect(x.clicked)
        for widget in self.basic_metadata_widgets:
            bc = getattr(widget, 'break_cycles', None)
            if bc is not None and callable(bc):
                bc()
        for widget in getattr(self, 'custom_metadata_widgets', []):
            widget.break_cycles()
Ejemplo n.º 36
0
    def initUI(self, gameboard):

        self.setStyleSheet("QWidget { background: #BCBCBC}")
        self.setFrameStyle(QFrame.Sunken | QFrame.StyledPanel)
        self.setFixedSize((gameboard.width - 1) * blockSize, 120)
        self.grid = QGridLayout()
        self.setLayout(self.grid)

        hbox = QHBoxLayout()
        vbox = QVBoxLayout()

        towerLabel = QLabel()
        towerLabel.setPixmap(QPixmap(os.path.join('./Pictures/', "tower.png")))

        vbox.addWidget(towerLabel)

        vbox.addLayout(hbox)
        self.grid.addLayout(vbox, 0, 0)

        towers = gameboard.getTowers()
        i = 0
        buttons = 0

        # We go through the list of different towers in the map and add buttons for them to the bottom left corner of the screen.
        while i < len(towers):
            if towers[i] == "t1":
                self.musketeerButton = BuyButton(
                    QPixmap(
                        os.path.join('./Pictures/',
                                     "musketeer_buybutton.png")),
                    QPixmap(
                        os.path.join('./Pictures/',
                                     "musketeer_buybutton_hover.png")),
                    QPixmap(
                        os.path.join('./Pictures/',
                                     "musketeer_buybutton_pressed.png")), self)
                self.musketeerButton.move(buttons * towerButtonSize + 10, 50)
                self.musketeerButton.clicked.connect(self.musketeerButtonClick)
                hbox.addWidget(self.musketeerButton)
                buttons += 1
            elif towers[i] == "t2":
                self.cannonButton = BuyButton(
                    QPixmap(os.path.join('./Pictures/',
                                         "cannon_buybutton.png")),
                    QPixmap(
                        os.path.join('./Pictures/',
                                     "cannon_buybutton_hovered.png")),
                    QPixmap(
                        os.path.join('./Pictures/',
                                     "cannon_buybutton_pressed.png")), self)
                self.cannonButton.move(buttons * towerButtonSize + 10, 50)
                self.cannonButton.clicked.connect(self.cannonButtonClick)
                hbox.addWidget(self.cannonButton)
                buttons += 1
            i += 1

        hbox.addStretch()
        '''
        slider2 = QSlider(Qt.Horizontal, self)
        slider2.setFocusPolicy(Qt.NoFocus)
        slider2.setSliderPosition(100 - self.parent.gameSpeed)
        #slider2.setGeometry(210, 140, 100, 20)
        slider2.valueChanged[int].connect(self.changeGameSpeed)
        
        hbox2 = QHBoxLayout()
        hbox2.addWidget(slider2)
        hbox2.addStretch()
        
        self.grid.addLayout(hbox2, 0, 1)
        '''

        hbox3 = QHBoxLayout()
        vbox3 = QVBoxLayout()
        hbox3.addStretch()

        self.lcd = QLCDNumber(self)

        vbox3.addStretch()
        vbox3.addWidget(self.lcd)
        vbox3.addStretch()

        self.pauseButton = QPushButton('Pause')
        self.pauseButton.clicked.connect(self.pauseGame)

        # I could add a restart button

        vbox3.addWidget(self.pauseButton)
        self.grid.addLayout(vbox3, 0, 2)

        self.show()
Ejemplo n.º 37
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
Ejemplo n.º 38
0
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

__author__ = 'ipetrash'

from PyQt5.Qt import QApplication, QLabel, QVBoxLayout, QWidget

app = QApplication([])

label_1 = QLabel()
label_1.setText('<i>Hello</i> <b>World</b><i><b>!!!</b><i>')

label_2 = QLabel()
label_2.setText('nothing...')

layout = QVBoxLayout()
layout.addWidget(label_1)
layout.addWidget(label_2)

mw = QWidget()
mw.setLayout(layout)
mw.show()

app.exec()
Ejemplo n.º 39
0
def ask_about_cc_mismatch(gui, db, newdb, missing_cols,
                          incompatible_cols):  # {{{
    source_metadata = db.field_metadata.custom_field_metadata(
        include_composites=True)
    ndbname = os.path.basename(newdb.library_path)

    d = QDialog(gui)
    d.setWindowTitle(_('Different custom columns'))
    l = QFormLayout()
    tl = QVBoxLayout()
    d.setLayout(tl)
    d.s = QScrollArea(d)
    tl.addWidget(d.s)
    d.w = QWidget(d)
    d.s.setWidget(d.w)
    d.s.setWidgetResizable(True)
    d.w.setLayout(l)
    d.setMinimumWidth(600)
    d.setMinimumHeight(500)
    d.bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)

    msg = _(
        'The custom columns in the <i>{0}</i> library are different from the '
        'custom columns in the <i>{1}</i> library. As a result, some metadata might not be copied.'
    ).format(os.path.basename(db.library_path), ndbname)
    d.la = la = QLabel(msg)
    la.setWordWrap(True)
    la.setStyleSheet('QLabel { margin-bottom: 1.5ex }')
    l.addRow(la)
    if incompatible_cols:
        la = d.la2 = QLabel(
            _('The following columns are incompatible - they have the same name'
              ' but different data types. They will be ignored: ') +
            ', '.join(sorted(incompatible_cols, key=sort_key)))
        la.setWordWrap(True)
        la.setStyleSheet('QLabel { margin-bottom: 1.5ex }')
        l.addRow(la)

    missing_widgets = []
    if missing_cols:
        la = d.la3 = QLabel(
            _('The following columns are missing in the <i>{0}</i> library.'
              ' You can choose to add them automatically below.').format(
                  ndbname))
        la.setWordWrap(True)
        l.addRow(la)
        for k in missing_cols:
            widgets = (k, QCheckBox(_('Add to the %s library') % ndbname))
            l.addRow(QLabel(k), widgets[1])
            missing_widgets.append(widgets)
    d.la4 = la = QLabel(
        _('This warning is only shown once per library, per session'))
    la.setWordWrap(True)
    tl.addWidget(la)

    tl.addWidget(d.bb)
    d.bb.accepted.connect(d.accept)
    d.bb.rejected.connect(d.reject)
    d.resize(d.sizeHint())
    if d.exec_() == d.Accepted:
        for k, cb in missing_widgets:
            if cb.isChecked():
                col_meta = source_metadata[k]
                newdb.create_custom_column(col_meta['label'], col_meta['name'],
                                           col_meta['datatype'],
                                           len(col_meta['is_multiple']) > 0,
                                           col_meta['is_editable'],
                                           col_meta['display'])
        return True
    return False
Ejemplo n.º 40
0
class TagBrowserWidget(QFrame):  # {{{
    def __init__(self, parent):
        QFrame.__init__(self, parent)
        self.setFrameStyle(QFrame.NoFrame if gprefs['tag_browser_old_look']
                           else QFrame.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.StyledPanel)
        l.setAutoFillBackground(True)
        l.setText(
            '<p><b>' +
            _('No more matches.</b><p> Click Find again to go to first match'))
        l.setAlignment(Qt.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.QueuedConnection)
        # The Alter Tag Browser button
        l = self.alter_tb
        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())

        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)

        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)

        # self.leak_test_timer = QTimer(self)
        # self.leak_test_timer.timeout.connect(self.test_for_leak)
        # self.leak_test_timer.start(5000)

    def save_state(self):
        gprefs.set('tag browser search box visible',
                   self.toggle_search_button.isChecked())

    def toggle_item(self):
        self.tags_view.toggle_current_index()

    def set_pane_is_visible(self, to_what):
        self.tags_view.set_pane_is_visible(to_what)

    def find_text_changed(self, str):
        self.current_find_position = None

    def set_focus_to_find_box(self):
        self.tb_bar.set_focus_to_find_box()

    def do_find(self, str=None):
        self.current_find_position = None
        self.find()

    @property
    def find_text(self):
        return unicode_type(self.item_search.currentText()).strip()

    def reset_find(self):
        model = self.tags_view.model()
        if model.get_categories_filter():
            model.set_categories_filter(None)
            self.tags_view.recount()
            self.current_find_position = None

    def find(self):
        model = self.tags_view.model()
        model.clear_boxed()

        # When a key is specified don't use the auto-collapsing search.
        # A colon separates the lookup key from the search string.
        # A leading colon says not to use autocollapsing search but search all keys
        txt = self.find_text
        colon = txt.find(':')
        if colon >= 0:
            key = self._parent.library_view.model().db.\
                        field_metadata.search_term_to_field_key(txt[:colon])
            if key in self._parent.library_view.model().db.field_metadata:
                txt = txt[colon + 1:]
            else:
                key = ''
                txt = txt[1:] if colon == 0 else txt
        else:
            key = None

        # key is None indicates that no colon was found.
        # key == '' means either a leading : was found or the key is invalid

        # At this point the txt might have a leading =, in which case do an
        # exact match search

        if (gprefs.get('tag_browser_always_autocollapse', False)
                and key is None and not txt.startswith('*')):
            txt = '*' + txt
        if txt.startswith('*'):
            self.tags_view.collapseAll()
            model.set_categories_filter(txt[1:])
            self.tags_view.recount()
            self.current_find_position = None
            return
        if model.get_categories_filter():
            model.set_categories_filter(None)
            self.tags_view.recount()
            self.current_find_position = None

        if not txt:
            return

        self.item_search.lineEdit().blockSignals(True)
        self.search_button.setFocus(True)
        self.item_search.lineEdit().blockSignals(False)

        if txt.startswith('='):
            equals_match = True
            txt = txt[1:]
        else:
            equals_match = False
        self.current_find_position = \
            model.find_item_node(key, txt, self.current_find_position,
                                 equals_match=equals_match)

        if self.current_find_position:
            self.tags_view.show_item_at_path(self.current_find_position,
                                             box=True)
        elif self.item_search.text():
            self.not_found_label.setVisible(True)
            if self.tags_view.verticalScrollBar().isVisible():
                sbw = self.tags_view.verticalScrollBar().width()
            else:
                sbw = 0
            width = self.width() - 8 - sbw
            height = self.not_found_label.heightForWidth(width) + 20
            self.not_found_label.resize(width, height)
            self.not_found_label.move(4, 10)
            self.not_found_label_timer.start(2000)

    def not_found_label_timer_event(self):
        self.not_found_label.setVisible(False)

    def keyPressEvent(self, ev):
        if ev.key() in (Qt.Key_Enter, Qt.Key_Return) and self.find_text:
            self.find()
            ev.accept()
            return
        return QFrame.keyPressEvent(self, ev)
Ejemplo n.º 41
0
class StartMenu(QWidget):
    
    def __init__(self, parent):
        super(StartMenu, self).__init__(parent)
        
        self.tophbox = QHBoxLayout()
        self.hbox = QHBoxLayout()
        self.vbox = QVBoxLayout()
        
        self.label = QLabel()
        self.label.setPixmap(QPixmap('img/new-game.png'))
        self.label.setScaledContents(True)
        self.label.setFixedSize(600, 200)
        self.tophbox.addWidget(self.label)
        
        self.startbutton = Button(self, 'Start')
        self.startbutton.setEnabled(False)
        self.startbutton.setFixedHeight(100)
        self.tophbox.addWidget(self.startbutton)
        
        self.playeramt_label = QLabel('Number of players:')
        self.playeramt_label.setFixedWidth(125)
        
        self.playeramount = QComboBox()
        self.playeramount.setStyleSheet('color: rgb(0, 0, 0)')
        self.playeramount.setFixedWidth(50)
        self.playeramount.addItems([str(i) for i in range(2, 13)])
        self.playeramount.setCurrentIndex(2)
        self.playeramount.setMaxVisibleItems(11)
        self.playeramount.currentTextChanged.connect(self.form_player_entries)
        
        self.score_label = QLabel('Score limit:')
        self.score_label.setFixedWidth(65)
        
        self.score_limit = QLineEdit()
        self.score_limit.setMaximumWidth(40)
        self.score_limit.setPalette(QPalette(Qt.white))
        self.score_limit.setText('16')
        
        self.mode_label = QLabel('Game Mode:')
        self.mode_label.setFixedWidth(85)
        
        self.mode_select = QComboBox()
        self.mode_select.addItems(['Deal-1', 'Deal-4'])
        self.mode_select.setPalette(QPalette(Qt.white))
        self.mode_select.setFixedWidth(100)
        self.mode_select.currentTextChanged.connect(self.update_playeramount)
            
        self.autofill_button = Button(self, 'Auto Fill')
        self.autofill_button.clicked.connect(self.auto_fill)
        self.clear_button = Button(self, 'Clear All')
        self.clear_button.clicked.connect(self.clear_all)
        
        self.player_entries = QVBoxLayout()
        
        self.spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
        
        self.hbox.addWidget(self.playeramt_label)
        self.hbox.addWidget(self.playeramount)
        self.hbox.addWidget(self.score_label)
        self.hbox.addWidget(self.score_limit)
        self.hbox.addWidget(self.mode_label)
        self.hbox.addWidget(self.mode_select)
        self.hbox.addWidget(self.autofill_button)
        self.hbox.addWidget(self.clear_button)
        
        self.vbox.addLayout(self.tophbox)
        self.vbox.addLayout(self.hbox)
        self.vbox.addLayout(self.player_entries)
        self.vbox.addItem(self.spacer)
        
        self.setLayout(self.vbox)
        
        self.form_player_entries() 


    def form_player_entries(self):
        amt = self.playeramount.currentIndex() + 2  #desired amount of player entries
        curr = self.player_entries.count()          #current amount of player entries
        dif = amt - self.player_entries.count()     #difference between desired and current entries
        
        if dif < 0:                                 #if too many entries currently 
            for i in range(curr-1, amt-1, -1):      #remove starting from last entry
                rm = self.player_entries.itemAt(i).widget()
                self.player_entries.removeWidget(rm)
                rm.setParent(None)
                
        
        else:                                       #if too few entries, add until desired amount reached
            for i in range(dif):
                new_entry = PlayerInfoField(self, self.player_entries.count())
                new_entry.name_field.textChanged.connect(self.check_filled)
                self.player_entries.addWidget(new_entry)
         
        self.check_filled()       
    
    
    def check_filled(self): #Enables start button when player fields are correctly filled
        ready = True
        
        for i in range(self.player_entries.count()):
            entry = self.player_entries.itemAt(i).widget()
            if entry.name_field.text() == '':
                ready = False
                break
            
        if ready:
            self.startbutton.setEnabled(True)
        else:
            self.startbutton.setEnabled(False)
            
                
    def auto_fill(self): #Generates fills the rest of the form automatically
        for i in range(self.player_entries.count()):
            entry = self.player_entries.itemAt(i).widget()
            if entry.name_field.text() == '':
                entry.generate_name()
                entry.AItoggle.setChecked(True)
                
    def clear_all(self):
        for i in range(self.player_entries.count()):
            entry = self.player_entries.itemAt(i).widget()
            entry.name_field.clear()
            entry.AItoggle.setChecked(False)
            
    def update_playeramount(self, mode):
        ind = self.playeramount.currentIndex()
        if mode == 'Deal-1': #Limit max players to 12
            self.playeramount.clear()
            self.playeramount.addItems([str(i) for i in range(2, 13)])
            self.playeramount.setCurrentIndex(ind)
            
        if mode == 'Deal-4': #Limit max players to 4
            self.playeramount.clear()
            self.playeramount.addItems([str(i) for i in range(2, 5)])
            self.playeramount.setCurrentIndex(min(2, ind))
            
        self.check_filled()
            
                
    def extract_info_and_init_game(self): #Creates a game object based on the info 
        pointlimit = int(self.score_limit.text())
        dealmode = self.mode_select.currentIndex()
        game = Game(pointlimit, dealmode)
        
        for i in range(self.player_entries.count()):
            entry = self.player_entries.itemAt(i).widget()
            name = entry.name_field.text()
            if entry.AItoggle.isChecked():
                difficulty = entry.AIdifficulty.currentIndex()
                game.add_player(name, difficulty)
            else:
                game.add_player(name, 5)
            
        return game
Ejemplo n.º 42
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(
             lopen(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(
             lopen(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.Ok)
     layout.addWidget(bx)
     bx.accepted.connect(d.accept)
     d.show()
Ejemplo n.º 43
0
    def request_trezor_init_settings(self, wizard, method, device):
        vbox = QVBoxLayout()
        next_enabled = True
        label = QLabel(_("Enter a label to name your device:"))
        name = QLineEdit()
        hl = QHBoxLayout()
        hl.addWidget(label)
        hl.addWidget(name)
        hl.addStretch(1)
        vbox.addLayout(hl)

        def clean_text(widget):
            text = widget.toPlainText().strip()
            return ' '.join(text.split())

        if method in [TIM_NEW, TIM_RECOVER]:
            gb = QGroupBox()
            hbox1 = QHBoxLayout()
            gb.setLayout(hbox1)
            # KeepKey recovery doesn't need a word count
            if method == TIM_NEW or self.device == 'TREZOR':
                vbox.addWidget(gb)
            gb.setTitle(_("Select your seed length:"))
            bg = QButtonGroup()
            for i, count in enumerate([12, 18, 24]):
                rb = QRadioButton(gb)
                rb.setText(_("%d words") % count)
                bg.addButton(rb)
                bg.setId(rb, i)
                hbox1.addWidget(rb)
                rb.setChecked(True)
            cb_pin = QCheckBox(_('Enable PIN protection'))
            cb_pin.setChecked(True)
        else:
            text = QTextEdit()
            text.setMaximumHeight(60)
            if method == TIM_MNEMONIC:
                msg = _("Enter your BIP39 mnemonic:")
            else:
                msg = _("Enter the master private key beginning with xprv:")

                def set_enabled():
                    from electrum_arg.keystore import is_xprv
                    wizard.next_button.setEnabled(is_xprv(clean_text(text)))

                text.textChanged.connect(set_enabled)
                next_enabled = False

            vbox.addWidget(QLabel(msg))
            vbox.addWidget(text)
            pin = QLineEdit()
            pin.setValidator(QRegExpValidator(QRegExp('[1-9]{0,10}')))
            pin.setMaximumWidth(100)
            hbox_pin = QHBoxLayout()
            hbox_pin.addWidget(QLabel(_("Enter your PIN (digits 1-9):")))
            hbox_pin.addWidget(pin)
            hbox_pin.addStretch(1)

        if method in [TIM_NEW, TIM_RECOVER]:
            vbox.addWidget(WWLabel(RECOMMEND_PIN))
            vbox.addWidget(cb_pin)
        else:
            vbox.addLayout(hbox_pin)

        passphrase_msg = WWLabel(PASSPHRASE_HELP_SHORT)
        passphrase_warning = WWLabel(PASSPHRASE_NOT_PIN)
        passphrase_warning.setStyleSheet("color: red")
        cb_phrase = QCheckBox(_('Enable passphrases'))
        cb_phrase.setChecked(False)
        vbox.addWidget(passphrase_msg)
        vbox.addWidget(passphrase_warning)
        vbox.addWidget(cb_phrase)

        wizard.exec_layout(vbox, next_enabled=next_enabled)

        if method in [TIM_NEW, TIM_RECOVER]:
            item = bg.checkedId()
            pin = cb_pin.isChecked()
        else:
            item = ' '.join(str(clean_text(text)).split())
            pin = str(pin.text())

        return (item, name.text(), pin, cb_phrase.isChecked())
Ejemplo n.º 44
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

        # The current database shown in the GUI
        # db is an instance of the class LibraryDatabase from db/legacy.py
        # This class has many, many methods that allow you to do a lot of
        # things. For most purposes you should use db.new_api, which has
        # a much nicer interface from db/cache.py
        self.db = gui.current_db

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

        self.label = QLabel(prefs['hello_world_msg'])
        self.l.addWidget(self.label)

        self.setWindowTitle('Interface Plugin Demo')
        self.setWindowIcon(icon)

        self.about_button = QPushButton('About', self)
        self.about_button.clicked.connect(self.about)
        self.l.addWidget(self.about_button)

        self.marked_button = QPushButton(
            'Show books with only one format in the calibre GUI', self)
        self.marked_button.clicked.connect(self.marked)
        self.l.addWidget(self.marked_button)

        self.view_button = QPushButton('View the most recently added book',
                                       self)
        self.view_button.clicked.connect(self.view)
        self.l.addWidget(self.view_button)

        self.update_metadata_button = QPushButton(
            'Update metadata in a book\'s files', self)
        self.update_metadata_button.clicked.connect(self.update_metadata)
        self.l.addWidget(self.update_metadata_button)

        self.conf_button = QPushButton('Configure this plugin', self)
        self.conf_button.clicked.connect(self.config)
        self.l.addWidget(self.conf_button)

        self.resize(self.sizeHint())

    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 Interface Plugin Demo',
                          text.decode('utf-8'))

    def marked(self):
        ''' Show books with only one format '''
        db = self.db.new_api
        matched_ids = {
            book_id
            for book_id in db.all_book_ids() if len(db.formats(book_id)) == 1
        }
        # Mark the records with the matching ids
        # new_api does not know anything about marked books, so we use the full
        # db object
        self.db.set_marked_ids(matched_ids)

        # Tell the GUI to search for all marked records
        self.gui.search.setEditText('marked:true')
        self.gui.search.do_search()

    def view(self):
        ''' View the most recently added book '''
        most_recent = most_recent_id = None
        db = self.db.new_api
        for book_id, timestamp in db.all_field_for('timestamp',
                                                   db.all_book_ids()).items():
            if most_recent is None or timestamp > most_recent:
                most_recent = timestamp
                most_recent_id = book_id

        if most_recent_id is not None:
            # Get a reference to the View plugin
            view_plugin = self.gui.iactions['View']
            # Ask the view plugin to launch the viewer for row_number
            view_plugin._view_calibre_books([most_recent_id])

    def update_metadata(self):
        '''
        Set the metadata in the files in the selected book's record to
        match the current metadata in the database.
        '''
        from calibre.ebooks.metadata.meta import set_metadata
        from calibre.gui2 import error_dialog, info_dialog

        # Get currently selected books
        rows = self.gui.library_view.selectionModel().selectedRows()
        if not rows or len(rows) == 0:
            return error_dialog(self.gui,
                                'Cannot update metadata',
                                'No books selected',
                                show=True)
        # Map the rows to book ids
        ids = list(map(self.gui.library_view.model().id, rows))
        db = self.db.new_api
        for book_id in ids:
            # Get the current metadata for this book from the db
            mi = db.get_metadata(book_id, get_cover=True, cover_as_data=True)
            fmts = db.formats(book_id)
            if not fmts:
                continue
            for fmt in fmts:
                fmt = fmt.lower()
                # Get a python file object for the format. This will be either
                # an in memory file or a temporary on disk file
                ffile = db.format(book_id, fmt, as_file=True)
                ffile.seek(0)
                # Set metadata in the format
                set_metadata(ffile, mi, fmt)
                ffile.seek(0)
                # Now replace the file in the calibre library with the updated
                # file. We dont use add_format_with_hooks as the hooks were
                # already run when the file was first added to calibre.
                db.add_format(book_id, fmt, ffile, run_hooks=False)

        info_dialog(self,
                    'Updated files',
                    'Updated the metadata in the files of %d book(s)' %
                    len(ids),
                    show=True)

    def config(self):
        self.do_user_config(parent=self)
        # Apply the changes
        self.label.setText(prefs['hello_world_msg'])
Ejemplo n.º 45
0
    def do_layout(self):
        self.central_widget.clear()
        self.tabs = []
        self.labels = []
        sto = QWidget.setTabOrder

        self.on_drag_enter.connect(self.handle_drag_enter)
        self.tabs.append(DragTrackingWidget(self, self.on_drag_enter))
        self.central_widget.addTab(self.tabs[0], _("&Metadata"))
        self.tabs[0].l = QGridLayout()
        self.tabs[0].setLayout(self.tabs[0].l)

        self.tabs.append(QWidget(self))
        self.central_widget.addTab(self.tabs[1], _("&Cover and formats"))
        self.tabs[1].l = QGridLayout()
        self.tabs[1].setLayout(self.tabs[1].l)

        # accept drop events so we can automatically switch to the second tab to
        # drop covers and formats
        self.tabs[0].setAcceptDrops(True)

        # Tab 0
        tab0 = self.tabs[0]

        tl = QGridLayout()
        gb = QGroupBox(_('&Basic metadata'), self.tabs[0])
        self.tabs[0].l.addWidget(gb, 0, 0, 1, 1)
        gb.setLayout(tl)

        self.button_box_layout.insertWidget(1, self.fetch_metadata_button)
        self.button_box_layout.insertWidget(2, self.config_metadata_button)
        sto(self.button_box, self.fetch_metadata_button)
        sto(self.fetch_metadata_button, self.config_metadata_button)
        sto(self.config_metadata_button, self.title)

        def create_row(row, widget, tab_to, button=None, icon=None, span=1):
            ql = BuddyLabel(widget)
            tl.addWidget(ql, row, 1, 1, 1)
            tl.addWidget(widget, row, 2, 1, 1)
            if button is not None:
                tl.addWidget(button, row, 3, span, 1)
                if icon is not None:
                    button.setIcon(QIcon(I(icon)))
            if tab_to is not None:
                if button is not None:
                    sto(widget, button)
                    sto(button, tab_to)
                else:
                    sto(widget, tab_to)

        tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1)
        tl.addWidget(self.manage_authors_button, 2, 0, 1, 1)
        tl.addWidget(self.paste_isbn_button, 12, 0, 1, 1)
        tl.addWidget(self.tags_editor_button, 6, 0, 1, 1)

        create_row(0, self.title, self.title_sort,
                   button=self.deduce_title_sort_button, span=2,
                   icon='auto_author_sort.png')
        create_row(1, self.title_sort, self.authors)
        create_row(2, self.authors, self.author_sort,
                   button=self.deduce_author_sort_button,
                   span=2, icon='auto_author_sort.png')
        create_row(3, self.author_sort, self.series)
        create_row(4, self.series, self.series_index,
                   button=self.clear_series_button, icon='trash.png')
        create_row(5, self.series_index, self.tags)
        create_row(6, self.tags, self.rating, button=self.clear_tags_button)
        create_row(7, self.rating, self.pubdate, button=self.clear_ratings_button)
        create_row(8, self.pubdate, self.publisher,
                   button=self.pubdate.clear_button, icon='trash.png')
        create_row(9, self.publisher, self.languages, button=self.publisher.clear_button, icon='trash.png')
        create_row(10, self.languages, self.timestamp)
        create_row(11, self.timestamp, self.identifiers,
                   button=self.timestamp.clear_button, icon='trash.png')
        create_row(12, self.identifiers, self.comments,
                   button=self.clear_identifiers_button, icon='trash.png')
        sto(self.clear_identifiers_button, self.swap_title_author_button)
        sto(self.swap_title_author_button, self.manage_authors_button)
        sto(self.manage_authors_button, self.tags_editor_button)
        sto(self.tags_editor_button, self.paste_isbn_button)
        tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding),
                   13, 1, 1 ,1)

        w = getattr(self, 'custom_metadata_widgets_parent', None)
        if w is not None:
            gb = QGroupBox(_('C&ustom metadata'), tab0)
            gbl = QVBoxLayout()
            gb.setLayout(gbl)
            sr = QScrollArea(tab0)
            sr.setWidgetResizable(True)
            sr.setFrameStyle(QFrame.NoFrame)
            sr.setWidget(w)
            gbl.addWidget(sr)
            self.tabs[0].l.addWidget(gb, 0, 1, 1, 1)
            sto(self.identifiers, gb)

        w = QGroupBox(_('&Comments'), tab0)
        sp = QSizePolicy()
        sp.setVerticalStretch(10)
        sp.setHorizontalPolicy(QSizePolicy.Expanding)
        sp.setVerticalPolicy(QSizePolicy.Expanding)
        w.setSizePolicy(sp)
        l = QHBoxLayout()
        w.setLayout(l)
        l.addWidget(self.comments)
        tab0.l.addWidget(w, 1, 0, 1, 2)

        # Tab 1
        tab1 = self.tabs[1]

        wsp = QWidget(tab1)
        wgl = QVBoxLayout()
        wsp.setLayout(wgl)

        # right-hand side of splitter
        gb = QGroupBox(_('Change cover'), tab1)
        l = QGridLayout()
        gb.setLayout(l)
        for i, b in enumerate(self.cover.buttons[:3]):
            l.addWidget(b, 0, i, 1, 1)
            sto(b, self.cover.buttons[i+1])
        hl = QHBoxLayout()
        for b in self.cover.buttons[3:]:
            hl.addWidget(b)
        sto(self.cover.buttons[-2], self.cover.buttons[-1])
        l.addLayout(hl, 1, 0, 1, 3)
        wgl.addWidget(gb)
        wgl.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding,
            QSizePolicy.Expanding))
        wgl.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding,
            QSizePolicy.Expanding))
        wgl.addWidget(self.formats_manager)

        self.splitter = QSplitter(Qt.Horizontal, tab1)
        tab1.l.addWidget(self.splitter)
        self.splitter.addWidget(self.cover)
        self.splitter.addWidget(wsp)

        self.formats_manager.formats.setMaximumWidth(10000)
        self.formats_manager.formats.setIconSize(QSize(64, 64))
Ejemplo n.º 46
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.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:
            self.restoreGeometry(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.scrollAreaWidgetContents = QWidget()
        self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 810, 494))
        self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
        self.verticalLayout_3 = QVBoxLayout(self.scrollAreaWidgetContents)
        self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.stack = QStackedWidget(self.scrollAreaWidgetContents)
        sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.stack.sizePolicy().hasHeightForWidth())
        self.stack.setSizePolicy(sizePolicy)
        self.stack.setObjectName("stack")
        self.page = QWidget()
        self.page.setObjectName("page")
        self.stack.addWidget(self.page)
        self.page_2 = QWidget()
        self.page_2.setObjectName("page_2")
        self.stack.addWidget(self.page_2)
        self.verticalLayout_3.addWidget(self.stack)
        self.scrollArea.setWidget(self.scrollAreaWidgetContents)
        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.setMaximumSize(QSize(16777215, 130))
        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 i in range(self.stack.count()):
            w = self.stack.widget(i)
            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.stack, 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.stack, 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.stack, self.plumber.get_option_by_name,
            self.plumber.get_option_help, self.db, self.book_id)
        while True:
            c = self.stack.currentWidget()
            if not c:
                break
            self.stack.removeWidget(c)

        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:
            self.stack.addWidget(w)
            w.set_help_signal.connect(self.help.setPlainText)

        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.stack.setCurrentIndex(idx)
        try:
            shutil.rmtree(self.plumber.archive_input_tdir, ignore_errors=True)
        except:
            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(
            list(map(unicode_type, [x.upper() for x in input_formats])))
        self.output_formats.addItems(
            list(map(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):
        self.stack.setCurrentIndex(index.row())

    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 i in range(self.stack.count()):
            w = self.stack.widget(i)
            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)
Ejemplo n.º 47
0
    def request_trezor_init_settings(self, wizard, method, model):
        vbox = QVBoxLayout()
        next_enabled = True
        label = QLabel(_("Enter a label to name your device:"))
        name = QLineEdit()
        hl = QHBoxLayout()
        hl.addWidget(label)
        hl.addWidget(name)
        hl.addStretch(1)
        vbox.addLayout(hl)

        def clean_text(widget):
            text = widget.toPlainText().strip()
            return ' '.join(text.split())

        if method in [TIM_NEW, TIM_RECOVER]:
            gb = QGroupBox()
            hbox1 = QHBoxLayout()
            gb.setLayout(hbox1)
            vbox.addWidget(gb)
            gb.setTitle(_("Select your seed length:"))
            bg_numwords = QButtonGroup()
            for i, count in enumerate([12, 18, 24]):
                rb = QRadioButton(gb)
                rb.setText(_("%d words") % count)
                bg_numwords.addButton(rb)
                bg_numwords.setId(rb, i)
                hbox1.addWidget(rb)
                rb.setChecked(True)
            cb_pin = QCheckBox(_('Enable PIN protection'))
            cb_pin.setChecked(True)
        else:
            text = QTextEdit()
            text.setMaximumHeight(60)
            if method == TIM_MNEMONIC:
                msg = _("Enter your BIP39 mnemonic:")
            else:
                msg = _("Enter the master private key beginning with xprv:")

                def set_enabled():
                    from electrum_nmc.keystore import is_xprv
                    wizard.next_button.setEnabled(is_xprv(clean_text(text)))

                text.textChanged.connect(set_enabled)
                next_enabled = False

            vbox.addWidget(QLabel(msg))
            vbox.addWidget(text)
            pin = QLineEdit()
            pin.setValidator(QRegExpValidator(QRegExp('[1-9]{0,9}')))
            pin.setMaximumWidth(100)
            hbox_pin = QHBoxLayout()
            hbox_pin.addWidget(QLabel(_("Enter your PIN (digits 1-9):")))
            hbox_pin.addWidget(pin)
            hbox_pin.addStretch(1)

        if method in [TIM_NEW, TIM_RECOVER]:
            vbox.addWidget(WWLabel(RECOMMEND_PIN))
            vbox.addWidget(cb_pin)
        else:
            vbox.addLayout(hbox_pin)

        passphrase_msg = WWLabel(PASSPHRASE_HELP_SHORT)
        passphrase_warning = WWLabel(PASSPHRASE_NOT_PIN)
        passphrase_warning.setStyleSheet("color: red")
        cb_phrase = QCheckBox(_('Enable passphrases'))
        cb_phrase.setChecked(False)
        vbox.addWidget(passphrase_msg)
        vbox.addWidget(passphrase_warning)
        vbox.addWidget(cb_phrase)

        # ask for recovery type (random word order OR matrix)
        if method == TIM_RECOVER and not model == 'T':
            gb_rectype = QGroupBox()
            hbox_rectype = QHBoxLayout()
            gb_rectype.setLayout(hbox_rectype)
            vbox.addWidget(gb_rectype)
            gb_rectype.setTitle(_("Select recovery type:"))
            bg_rectype = QButtonGroup()

            rb1 = QRadioButton(gb_rectype)
            rb1.setText(_('Scrambled words'))
            bg_rectype.addButton(rb1)
            bg_rectype.setId(rb1, RECOVERY_TYPE_SCRAMBLED_WORDS)
            hbox_rectype.addWidget(rb1)
            rb1.setChecked(True)

            rb2 = QRadioButton(gb_rectype)
            rb2.setText(_('Matrix'))
            bg_rectype.addButton(rb2)
            bg_rectype.setId(rb2, RECOVERY_TYPE_MATRIX)
            hbox_rectype.addWidget(rb2)
        else:
            bg_rectype = None

        wizard.exec_layout(vbox, next_enabled=next_enabled)

        if method in [TIM_NEW, TIM_RECOVER]:
            item = bg_numwords.checkedId()
            pin = cb_pin.isChecked()
            recovery_type = bg_rectype.checkedId() if bg_rectype else None
        else:
            item = ' '.join(str(clean_text(text)).split())
            pin = str(pin.text())
            recovery_type = None

        return (item, name.text(), pin, cb_phrase.isChecked(), recovery_type)
class ExtraCustomization(DeviceConfigTab):  # {{{
    def __init__(self, extra_customization_message,
                 extra_customization_choices, device_settings):
        super(ExtraCustomization, self).__init__()

        debug_print(
            "ExtraCustomization.__init__ - extra_customization_message=",
            extra_customization_message)
        debug_print(
            "ExtraCustomization.__init__ - extra_customization_choices=",
            extra_customization_choices)
        debug_print(
            "ExtraCustomization.__init__ - device_settings.extra_customization=",
            device_settings.extra_customization)
        debug_print("ExtraCustomization.__init__ - device_settings=",
                    device_settings)
        self.extra_customization_message = extra_customization_message

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

        options_group = QGroupBox(_("Extra driver customization options"),
                                  self)
        self.l.addWidget(options_group)
        self.extra_layout = QGridLayout()
        self.extra_layout.setObjectName("extra_layout")
        options_group.setLayout(self.extra_layout)

        if extra_customization_message:
            extra_customization_choices = extra_customization_choices or {}

            def parse_msg(m):
                msg, _, tt = m.partition(':::') if m else ('', '', '')
                return msg.strip(), textwrap.fill(tt.strip(), 100)

            if isinstance(extra_customization_message, list):
                self.opt_extra_customization = []
                if len(extra_customization_message) > 6:
                    row_func = lambda x, y: ((x / 2) * 2) + y
                    col_func = lambda x: x % 2
                else:
                    row_func = lambda x, y: x * 2 + y
                    col_func = lambda x: 0

                for i, m in enumerate(extra_customization_message):
                    label_text, tt = parse_msg(m)
                    if not label_text:
                        self.opt_extra_customization.append(None)
                        continue
                    if isinstance(device_settings.extra_customization[i],
                                  bool):
                        self.opt_extra_customization.append(
                            QCheckBox(label_text))
                        self.opt_extra_customization[-1].setToolTip(tt)
                        self.opt_extra_customization[i].setChecked(
                            bool(device_settings.extra_customization[i]))
                    elif i in extra_customization_choices:
                        cb = QComboBox(self)
                        self.opt_extra_customization.append(cb)
                        l = QLabel(label_text)
                        l.setToolTip(tt), cb.setToolTip(tt), l.setBuddy(
                            cb), cb.setToolTip(tt)
                        for li in sorted(extra_customization_choices[i]):
                            self.opt_extra_customization[i].addItem(li)
                        cb.setCurrentIndex(
                            max(
                                0,
                                cb.findText(
                                    device_settings.extra_customization[i])))
                    else:
                        self.opt_extra_customization.append(QLineEdit(self))
                        l = QLabel(label_text)
                        l.setToolTip(tt)
                        self.opt_extra_customization[i].setToolTip(tt)
                        l.setBuddy(self.opt_extra_customization[i])
                        l.setWordWrap(True)
                        self.opt_extra_customization[i].setText(
                            device_settings.extra_customization[i])
                        self.opt_extra_customization[i].setCursorPosition(0)
                        self.extra_layout.addWidget(l, row_func(i + 2, 0),
                                                    col_func(i))
                    self.extra_layout.addWidget(
                        self.opt_extra_customization[i], row_func(i + 2, 1),
                        col_func(i))
                spacerItem1 = QSpacerItem(10, 10, QSizePolicy.Minimum,
                                          QSizePolicy.Expanding)
                self.extra_layout.addItem(spacerItem1, row_func(i + 2 + 2, 1),
                                          0, 1, 2)
                self.extra_layout.setRowStretch(row_func(i + 2 + 2, 1), 2)
            else:
                self.opt_extra_customization = QLineEdit()
                label_text, tt = parse_msg(extra_customization_message)
                l = QLabel(label_text)
                l.setToolTip(tt)
                l.setBuddy(self.opt_extra_customization)
                l.setWordWrap(True)
                if device_settings.extra_customization:
                    self.opt_extra_customization.setText(
                        device_settings.extra_customization)
                    self.opt_extra_customization.setCursorPosition(0)
                self.opt_extra_customization.setCursorPosition(0)
                self.extra_layout.addWidget(l, 0, 0)
                self.extra_layout.addWidget(self.opt_extra_customization, 1, 0)

    def extra_customization(self):
        ec = []
        if self.extra_customization_message:
            if isinstance(self.extra_customization_message, list):
                for i in range(0, len(self.extra_customization_message)):
                    if self.opt_extra_customization[i] is None:
                        ec.append(None)
                        continue
                    if hasattr(self.opt_extra_customization[i], 'isChecked'):
                        ec.append(self.opt_extra_customization[i].isChecked())
                    elif hasattr(self.opt_extra_customization[i],
                                 'currentText'):
                        ec.append(
                            unicode(self.opt_extra_customization[i].
                                    currentText()).strip())
                    else:
                        ec.append(
                            unicode(self.opt_extra_customization[i].text()).
                            strip())
            else:
                ec = unicode(self.opt_extra_customization.text()).strip()
                if not ec:
                    ec = None

        return ec

    @property
    def has_extra_customizations(self):
        debug_print(
            "ExtraCustomization::has_extra_customizations - self.extra_customization_message",
            self.extra_customization_message)
        return self.extra_customization_message and len(
            self.extra_customization_message) > 0
Ejemplo n.º 49
0
    def __init__(self, handler, data):
        '''Ask user for 2nd factor authentication. Support text, security card and paired mobile methods.
        Use last method from settings, but support new pairing and downgrade.
        '''
        QDialog.__init__(self, handler.top_level_window())
        self.handler = handler
        self.txdata = data
        self.idxs = self.txdata[
            'keycardData'] if self.txdata['confirmationType'] > 1 else ''
        self.setMinimumWidth(650)
        self.setWindowTitle(_("Ledger Wallet Authentication"))
        self.cfg = copy.deepcopy(self.handler.win.wallet.get_keystore().cfg)
        self.dongle = self.handler.win.wallet.get_keystore().get_client(
        ).dongle
        self.ws = None
        self.pin = ''

        self.devmode = self.getDevice2FAMode()
        if self.devmode == 0x11 or self.txdata['confirmationType'] == 1:
            self.cfg['mode'] = 0

        vbox = QVBoxLayout()
        self.setLayout(vbox)

        def on_change_mode(idx):
            if idx < 2 and self.ws:
                self.ws.stop()
                self.ws = None
            self.cfg[
                'mode'] = 0 if self.devmode == 0x11 else idx if idx > 0 else 1
            if self.cfg['mode'] > 1 and self.cfg['pair'] and not self.ws:
                self.req_validation()
            if self.cfg['mode'] > 0:
                self.handler.win.wallet.get_keystore().cfg = self.cfg
                self.handler.win.wallet.save_keystore()
            self.update_dlg()

        def add_pairing():
            self.do_pairing()

        def return_pin():
            self.pin = self.pintxt.text(
            ) if self.txdata['confirmationType'] == 1 else self.cardtxt.text()
            if self.cfg['mode'] == 1:
                self.pin = ''.join(chr(int(str(i), 16)) for i in self.pin)
            self.accept()

        self.modebox = QWidget()
        modelayout = QHBoxLayout()
        self.modebox.setLayout(modelayout)
        modelayout.addWidget(QLabel(_("Method:")))
        self.modes = QComboBox()
        modelayout.addWidget(self.modes, 2)
        self.addPair = QPushButton(_("Pair"))
        self.addPair.setMaximumWidth(60)
        modelayout.addWidget(self.addPair)
        modelayout.addStretch(1)
        self.modebox.setMaximumHeight(50)
        vbox.addWidget(self.modebox)

        self.populate_modes()
        self.modes.currentIndexChanged.connect(on_change_mode)
        self.addPair.clicked.connect(add_pairing)

        self.helpmsg = QTextEdit()
        self.helpmsg.setStyleSheet(
            "QTextEdit { background-color: lightgray; }")
        self.helpmsg.setReadOnly(True)
        vbox.addWidget(self.helpmsg)

        self.pinbox = QWidget()
        pinlayout = QHBoxLayout()
        self.pinbox.setLayout(pinlayout)
        self.pintxt = QLineEdit()
        self.pintxt.setEchoMode(2)
        self.pintxt.setMaxLength(4)
        self.pintxt.returnPressed.connect(return_pin)
        pinlayout.addWidget(QLabel(_("Enter PIN:")))
        pinlayout.addWidget(self.pintxt)
        pinlayout.addWidget(QLabel(_("NOT DEVICE PIN - see above")))
        pinlayout.addStretch(1)
        self.pinbox.setVisible(self.cfg['mode'] == 0)
        vbox.addWidget(self.pinbox)

        self.cardbox = QWidget()
        card = QVBoxLayout()
        self.cardbox.setLayout(card)
        self.addrtext = QTextEdit()
        self.addrtext.setStyleSheet(
            "QTextEdit { color:blue; background-color:lightgray; padding:15px 10px; border:none; font-size:20pt; font-family:monospace; }"
        )
        self.addrtext.setReadOnly(True)
        self.addrtext.setMaximumHeight(130)
        card.addWidget(self.addrtext)

        def pin_changed(s):
            if len(s) < len(self.idxs):
                i = self.idxs[len(s)]
                addr = self.txdata['address']
                if not constants.net.TESTNET:
                    text = addr[:i] + '<u><b>' + addr[
                        i:i + 1] + '</u></b>' + addr[i + 1:]
                else:
                    # pin needs to be created from mainnet address
                    addr_mainnet = bitcoin.script_to_address(
                        bitcoin.address_to_script(addr),
                        net=constants.BitcoinMainnet)
                    addr_mainnet = addr_mainnet[:i] + '<u><b>' + addr_mainnet[
                        i:i + 1] + '</u></b>' + addr_mainnet[i + 1:]
                    text = str(addr) + '\n' + str(addr_mainnet)
                self.addrtext.setHtml(str(text))
            else:
                self.addrtext.setHtml(_("Press Enter"))

        pin_changed('')
        cardpin = QHBoxLayout()
        cardpin.addWidget(QLabel(_("Enter PIN:")))
        self.cardtxt = QLineEdit()
        self.cardtxt.setEchoMode(2)
        self.cardtxt.setMaxLength(len(self.idxs))
        self.cardtxt.textChanged.connect(pin_changed)
        self.cardtxt.returnPressed.connect(return_pin)
        cardpin.addWidget(self.cardtxt)
        cardpin.addWidget(QLabel(_("NOT DEVICE PIN - see above")))
        cardpin.addStretch(1)
        card.addLayout(cardpin)
        self.cardbox.setVisible(self.cfg['mode'] == 1)
        vbox.addWidget(self.cardbox)

        self.pairbox = QWidget()
        pairlayout = QVBoxLayout()
        self.pairbox.setLayout(pairlayout)
        pairhelp = QTextEdit(helpTxt[5])
        pairhelp.setStyleSheet("QTextEdit { background-color: lightgray; }")
        pairhelp.setReadOnly(True)
        pairlayout.addWidget(pairhelp, 1)
        self.pairqr = QRCodeWidget()
        pairlayout.addWidget(self.pairqr, 4)
        self.pairbox.setVisible(False)
        vbox.addWidget(self.pairbox)
        self.update_dlg()

        if self.cfg['mode'] > 1 and not self.ws:
            self.req_validation()
Ejemplo n.º 50
0
    def _initialize_controls(self):
        self.setWindowTitle(_('User plugins'))
        self.setWindowIcon(QIcon(I('plugins/plugin_updater.png')))
        layout = QVBoxLayout(self)
        self.setLayout(layout)
        title_layout = ImageTitleLayout(self, 'plugins/plugin_updater.png',
                                        _('User plugins'))
        layout.addLayout(title_layout)

        header_layout = QHBoxLayout()
        layout.addLayout(header_layout)
        self.filter_combo = PluginFilterComboBox(self)
        self.filter_combo.setMinimumContentsLength(20)
        self.filter_combo.currentIndexChanged[int].connect(
            self._filter_combo_changed)
        la = QLabel(_('Filter list of &plugins') + ':', self)
        la.setBuddy(self.filter_combo)
        header_layout.addWidget(la)
        header_layout.addWidget(self.filter_combo)
        header_layout.addStretch(10)

        # filter plugins by name
        la = QLabel(_('Filter by &name') + ':', self)
        header_layout.addWidget(la)
        self.filter_by_name_lineedit = QLineEdit(self)
        la.setBuddy(self.filter_by_name_lineedit)
        self.filter_by_name_lineedit.setText("")
        self.filter_by_name_lineedit.textChanged.connect(
            self._filter_name_lineedit_changed)

        header_layout.addWidget(self.filter_by_name_lineedit)

        self.plugin_view = QTableView(self)
        self.plugin_view.horizontalHeader().setStretchLastSection(True)
        self.plugin_view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.plugin_view.setSelectionMode(QAbstractItemView.SingleSelection)
        self.plugin_view.setAlternatingRowColors(True)
        self.plugin_view.setSortingEnabled(True)
        self.plugin_view.setIconSize(QSize(28, 28))
        layout.addWidget(self.plugin_view)

        details_layout = QHBoxLayout()
        layout.addLayout(details_layout)
        forum_label = self.forum_label = QLabel('')
        forum_label.setTextInteractionFlags(Qt.LinksAccessibleByMouse
                                            | Qt.LinksAccessibleByKeyboard)
        forum_label.linkActivated.connect(self._forum_label_activated)
        details_layout.addWidget(QLabel(_('Description') + ':', self), 0,
                                 Qt.AlignLeft)
        details_layout.addWidget(forum_label, 1, Qt.AlignRight)

        self.description = QLabel(self)
        self.description.setFrameStyle(QFrame.Panel | QFrame.Sunken)
        self.description.setAlignment(Qt.AlignTop | Qt.AlignLeft)
        self.description.setMinimumHeight(40)
        self.description.setWordWrap(True)
        layout.addWidget(self.description)

        self.button_box = QDialogButtonBox(QDialogButtonBox.Close)
        self.button_box.rejected.connect(self.reject)
        self.finished.connect(self._finished)
        self.install_button = self.button_box.addButton(
            _('&Install'), QDialogButtonBox.AcceptRole)
        self.install_button.setToolTip(_('Install the selected plugin'))
        self.install_button.clicked.connect(self._install_clicked)
        self.install_button.setEnabled(False)
        self.configure_button = self.button_box.addButton(
            ' ' + _('&Customize plugin ') + ' ', QDialogButtonBox.ResetRole)
        self.configure_button.setToolTip(
            _('Customize the options for this plugin'))
        self.configure_button.clicked.connect(self._configure_clicked)
        self.configure_button.setEnabled(False)
        layout.addWidget(self.button_box)
Ejemplo n.º 51
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(
                lopen(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(
                lopen(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.Ok)
        layout.addWidget(bx)
        bx.accepted.connect(d.accept)
        b = bx.addButton(_('&Clear logs'), bx.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()
Ejemplo n.º 52
0
class ConfigWidget(QWidget):

    def __init__(self, ia):
        QWidget.__init__(self)
        self.ia = ia
        self.layout = QVBoxLayout()
        self.setLayout(self.layout)

        # make the config menu as a widget
        self.config_menu = self.make_menu()

        # make a scroll area to hold the menu and add it to the layout
        self.scroll = QScrollArea()
        self.scroll.setWidget(self.config_menu)
        self.layout.addWidget(self.scroll)

    def save_settings(self):
        for group in config:
            if group["Type"] == "columnboxes":
                func = self.CustomColumnComboBox.get_selected_column
            else:
                func = QCheckBox.isChecked
            for item in group["Items"]:
                name = getattr(self, item[CONFIG_NAME])
                prefs[item[CONFIG_NAME]] = func(name)
        # rebuild the menu
        self.ia.toggle_menu_items()

    def make_menu(self):
        config_menu = QWidget()
        self.l = QVBoxLayout()
        config_menu.setLayout(self.l)

        # add the menu items
        for group in config:
            self.make_submenu(group, self.l)

        return config_menu

    def make_submenu(self, group, parent):
        lo = self.make_groupbox(group, parent)

        # get the right builder function for the group type
        if group["Type"] == "checkboxes":
            func = partial(self.make_checkbox, lo, group["Columns"])
        else:
            func = partial(self.make_columnbox, lo, group["Columns"])

        # loop through the items and build the entries
        grid_row, grid_column = 1, 0
        for item in group["Items"]:
            grid_row, grid_column = func(item, grid_row, grid_column)

        # make buttons exclusive
        if "Exclusive_Items" in group:
            for item_list in group["Exclusive_Items"]:
                self.make_exclusive(item_list)

    def make_exclusive(self, item_list):
        excl_group = QButtonGroup(self)
        for item in item_list:
            item = getattr(self, item)
            excl_group.addButton(item)

    def make_groupbox(self, group, parent):
        groupbox = QGroupBox(group["Title"], self)
        setattr(self, group["Name"] + "_box", groupbox)
        parent.addWidget(groupbox)
        groupbox_layout = QGridLayout()
        setattr(self, group["Name"] + "_layout", groupbox_layout)
        groupbox.setLayout(groupbox_layout)
        return groupbox_layout

    def make_checkbox(self, parent, columns, item, grid_row, grid_column):
        checkbox = QCheckBox(item[CONFIG_TITLE], self)
        setattr(self, item[CONFIG_NAME], checkbox)
        checkbox.setChecked(prefs[item[CONFIG_NAME]])
        parent.addWidget(checkbox, grid_row, grid_column)

        # check for new row
        if grid_column < columns - 1:
            return grid_row, grid_column + 1
        return grid_row + 1, 0

    def make_columnbox(self, parent, columns, item, grid_row, grid_column):
        # label
        column_label = QLabel(item[CONFIG_TITLE], self)
        setattr(self, item[CONFIG_NAME] + "label", column_label)

        # columnbox
        available_columns = self.get_custom_columns(item[CONFIG_COLUMN_TYPE])
        column_box = self.CustomColumnComboBox(self, available_columns, prefs[item[CONFIG_NAME]])
        setattr(self, item[CONFIG_NAME], column_box)

        # put together and add
        column_label.setBuddy(column_box)
        parent.addWidget(column_label, grid_row, grid_column)
        parent.addWidget(column_box, grid_row, grid_column + 1)

        # check for new row
        if grid_column < columns / 2:
            return grid_row, grid_column + 2
        return grid_row + 1, 0

    def get_custom_columns(self, column_type):
        '''
        Gets matching custom columns for column_type
        '''
        custom_columns = self.ia.gui.library_view.model().custom_columns
        available_columns = {}
        for key, column in custom_columns.iteritems():
            if (column["datatype"] in column_type["datatype"] and
                    bool(column["is_multiple"]) == column_type["is_multiple"] and
                    column['display'].get('is_names', False) == column_type['is_names']):
                available_columns[key] = column
        return available_columns

    # modified from CountPages
    class CustomColumnComboBox(QComboBox):

        def __init__(self, parent, custom_columns={}, selected_column=''):
            QComboBox.__init__(self, parent)
            self.populate_combo(custom_columns, selected_column)

        def populate_combo(self, custom_columns, selected_column):
            self.clear()
            self.column_names = []
            selected_idx = 0
            custom_columns[""] = {"name": ""}
            for key in sorted(custom_columns.keys()):
                self.column_names.append(key)
                self.addItem('%s' % (custom_columns[key]['name']))
                if key == selected_column:
                    selected_idx = len(self.column_names) - 1
            self.setCurrentIndex(selected_idx)

        def select_column(self, key):
            selected_idx = 0
            for i, val in enumerate(self.column_names):
                if val == key:
                    selected_idx = i
                    break
            self.setCurrentIndex(selected_idx)

        def get_selected_column(self):
            return self.column_names[self.currentIndex()]
Ejemplo n.º 53
0
    def setup_ui(self):
        self.quote_for_trad_target = _("Update quotes: "",'' -> 「」,『』")
        self.quote_for_simp_target = _("Update quotes: 「」,『』 -> "",''")

        # Create layout for entire dialog
        layout = QVBoxLayout(self)
        self.setLayout(layout)

        #Create a scroll area for the top part of the dialog
        self.scrollArea = QScrollArea(self)
        self.scrollArea.setWidgetResizable(True)

        # Create widget for all the contents of the dialog except the OK and Cancel buttons
        self.scrollContentWidget = QWidget(self.scrollArea)
        self.scrollArea.setWidget(self.scrollContentWidget)
        widgetLayout = QVBoxLayout(self.scrollContentWidget)

        # Add scrollArea to dialog
        layout.addWidget(self.scrollArea)

        self.operation_group_box = QGroupBox(_('Conversion Direction'))
        widgetLayout.addWidget(self.operation_group_box)
        operation_group_box_layout = QVBoxLayout()
        self.operation_group_box.setLayout(operation_group_box_layout)

        operation_group = QButtonGroup(self)
        self.no_conversion_button = QRadioButton(_('No Conversion'))
        operation_group.addButton(self.no_conversion_button)
        self.trad_to_simp_button = QRadioButton(_('Traditional to Simplified'))
        operation_group.addButton(self.trad_to_simp_button)
        self.simp_to_trad_button = QRadioButton(_('Simplified to Traditional'))
        operation_group.addButton(self.simp_to_trad_button)
        self.trad_to_trad_button = QRadioButton(
            _('Traditional to Traditional'))
        operation_group.addButton(self.trad_to_trad_button)
        operation_group_box_layout.addWidget(self.no_conversion_button)
        operation_group_box_layout.addWidget(self.trad_to_simp_button)
        operation_group_box_layout.addWidget(self.simp_to_trad_button)
        operation_group_box_layout.addWidget(self.trad_to_trad_button)
        self.no_conversion_button.toggled.connect(self.update_gui)
        self.trad_to_simp_button.toggled.connect(self.update_gui)
        self.simp_to_trad_button.toggled.connect(self.update_gui)
        self.trad_to_trad_button.toggled.connect(self.update_gui)

        self.style_group_box = QGroupBox(_('Language Styles'))
        widgetLayout.addWidget(self.style_group_box)
        style_group_box_layout = QVBoxLayout()
        self.style_group_box.setLayout(style_group_box_layout)

        input_layout = QHBoxLayout()
        style_group_box_layout.addLayout(input_layout)
        self.input_region_label = QLabel(_('Input:'))
        input_layout.addWidget(self.input_region_label)
        self.input_combo = QComboBox()
        input_layout.addWidget(self.input_combo)
        self.input_combo.addItems([_('Mainland'), _('Hong Kong'), _('Taiwan')])
        self.input_combo.setToolTip(_('Select the origin region of the input'))
        self.input_combo.currentIndexChanged.connect(self.update_gui)

        output_layout = QHBoxLayout()
        style_group_box_layout.addLayout(output_layout)
        self.output_region_label = QLabel(_('Output:'))
        output_layout.addWidget(self.output_region_label)
        self.output_combo = QComboBox()
        output_layout.addWidget(self.output_combo)
        self.output_combo.addItems(
            [_('Mainland'), _('Hong Kong'),
             _('Taiwan')])
        self.output_combo.setToolTip(
            _('Select the desired region of the output'))
        self.output_combo.currentIndexChanged.connect(self.update_gui)

        self.use_target_phrases = QCheckBox(
            _('Use output target phrases if possible'))
        self.use_target_phrases.setToolTip(
            _('Check to allow region specific word replacements if available'))
        style_group_box_layout.addWidget(self.use_target_phrases)
        self.use_target_phrases.stateChanged.connect(self.update_gui)

        self.quotation_group_box = QGroupBox(_('Quotation Marks'))
        widgetLayout.addWidget(self.quotation_group_box)
        quotation_group_box_layout = QVBoxLayout()
        self.quotation_group_box.setLayout(quotation_group_box_layout)

        quotation_group = QButtonGroup(self)
        self.quotation_no_conversion_button = QRadioButton(_('No Conversion'))
        quotation_group.addButton(self.quotation_no_conversion_button)
        self.quotation_trad_to_simp_button = QRadioButton(
            self.quote_for_simp_target)
        quotation_group.addButton(self.quotation_trad_to_simp_button)
        self.quotation_simp_to_trad_button = QRadioButton(
            self.quote_for_trad_target)
        quotation_group.addButton(self.quotation_simp_to_trad_button)
        quotation_group_box_layout.addWidget(
            self.quotation_no_conversion_button)
        quotation_group_box_layout.addWidget(
            self.quotation_simp_to_trad_button)
        quotation_group_box_layout.addWidget(
            self.quotation_trad_to_simp_button)
        self.quotation_no_conversion_button.toggled.connect(self.update_gui)
        self.quotation_trad_to_simp_button.toggled.connect(self.update_gui)
        self.quotation_simp_to_trad_button.toggled.connect(self.update_gui)
        self.use_smart_quotes = QCheckBox(
            """Use curved 'Smart" quotes if applicable""")
        self.use_smart_quotes.setToolTip(
            _('Use smart curved half-width quotes rather than straight full-width quotes'
              ))
        quotation_group_box_layout.addWidget(self.use_smart_quotes)
        self.use_smart_quotes.stateChanged.connect(self.update_gui)

        self.other_group_box = QGroupBox(_('Other Changes'))
        widgetLayout.addWidget(self.other_group_box)
        other_group_box_layout = QVBoxLayout()
        self.other_group_box.setLayout(other_group_box_layout)

        text_dir_layout = QHBoxLayout()
        other_group_box_layout.addLayout(text_dir_layout)
        direction_label = QLabel(_('Text Direction:'))
        text_dir_layout.addWidget(direction_label)
        self.text_dir_combo = QComboBox()
        text_dir_layout.addWidget(self.text_dir_combo)
        self.text_dir_combo.addItems(
            [_('No Conversion'),
             _('Horizontal'),
             _('Vertical')])
        self.text_dir_combo.setToolTip(
            _('Select the desired text orientation'))
        self.text_dir_combo.currentIndexChanged.connect(self.update_gui)

        self.optimization_group_box = QGroupBox(
            _('Reader Device Optimization'))
        other_group_box_layout.addWidget(self.optimization_group_box)
        optimization_group_box_layout = QVBoxLayout()
        self.optimization_group_box.setLayout(optimization_group_box_layout)

        punc_group = QButtonGroup(self)
        self.text_dir_punc_none_button = QRadioButton(
            """No presentation optimization""")
        optimization_group_box_layout.addWidget(self.text_dir_punc_none_button)
        self.text_dir_punc_button = QRadioButton(
            """Optimize presentation for Readium reader""")
        self.text_dir_punc_button.setToolTip(
            _('Use vert/horiz punctuation presentation forms for Chrome Readium Epub3 reader'
              ))
        optimization_group_box_layout.addWidget(self.text_dir_punc_button)
        self.text_dir_punc_kindle_button = QRadioButton(
            """Optimize presentation for Kindle reader""")
        self.text_dir_punc_kindle_button.setToolTip(
            _('Use vert/horiz puncuation presentation forms for Kindle reader')
        )
        optimization_group_box_layout.addWidget(
            self.text_dir_punc_kindle_button)
        self.text_dir_punc_none_button.toggled.connect(self.update_gui)
        self.text_dir_punc_button.toggled.connect(self.update_gui)
        self.text_dir_punc_kindle_button.toggled.connect(self.update_gui)

        source_group = QButtonGroup(self)
        self.file_source_button = QRadioButton(_('Selected File Only'))
        self.book_source_button = QRadioButton(_('Entire eBook'))
        source_group.addButton(self.file_source_button)
        source_group.addButton(self.book_source_button)
        self.source_group_box = QGroupBox(_('Source'))
        if not self.force_entire_book:
            widgetLayout.addWidget(self.source_group_box)
            source_group_box_layout = QVBoxLayout()
            self.source_group_box.setLayout(source_group_box_layout)
            source_group_box_layout.addWidget(self.file_source_button)
            source_group_box_layout.addWidget(self.book_source_button)

        layout.addSpacing(10)
        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok
                                           | QDialogButtonBox.Cancel)

        self.button_box.accepted.connect(self._ok_clicked)
        self.button_box.rejected.connect(self.reject)
        layout.addWidget(self.button_box)

        self.input_combo.setCurrentIndex(self.prefs['input_format'])
        self.output_combo.setCurrentIndex(self.prefs['output_format'])
        self.no_conversion_button.setChecked(self.prefs['no_conversion'])
        self.trad_to_simp_button.setChecked(self.prefs['trad_to_simp'])
        self.simp_to_trad_button.setChecked(self.prefs['simp_to_trad'])
        self.trad_to_trad_button.setChecked(self.prefs['trad_to_trad'])
        if not self.force_entire_book:
            self.file_source_button.setChecked(self.prefs['use_html_file'])
            self.book_source_button.setChecked(self.prefs['use_entire_book'])
        else:
            self.file_source_button.setChecked(False)
            self.book_source_button.setChecked(True)

        self.quotation_no_conversion_button.setChecked(
            self.prefs['quote_no_conversion'])
        self.quotation_trad_to_simp_button.setChecked(
            self.prefs['quote_trad_to_simp'])
        self.quotation_simp_to_trad_button.setChecked(
            self.prefs['quote_simp_to_trad'])

        self.use_smart_quotes.setChecked(self.prefs['use_smart_quotes'])
        self.text_dir_combo.setCurrentIndex(self.prefs['orientation'])
        self.text_dir_punc_none_button.setChecked(
            self.prefs['no_optimization'])
        self.text_dir_punc_button.setChecked(
            self.prefs['readium_optimization'])
        self.text_dir_punc_kindle_button.setChecked(
            self.prefs['kindle_optimization'])
        self.update_gui()
Ejemplo n.º 54
0
class ConfigWidget(QWidget):
    def __init__(self):
        self.plugin_prefs = prefs
        self.restart_required = False
        self.inspector = None

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

        #add checkbox
        self.inspector_checkbox = QCheckBox('Show Inspector')
        self.inspector_checkbox.setChecked(
            self.plugin_prefs.get('inspector_enabled', False))
        self.inspector_checkbox.stateChanged.connect(
            self._inspector_enabled_changed)
        self.inspector_checkbox.setToolTip(
            'Enable Javascript Console for debugging WebView requests to QuietThyme API'
        )
        self.l.addWidget(self.inspector_checkbox)

        self.beta_checkbox = QCheckBox('Beta Mode')
        self.beta_checkbox.setChecked(self.plugin_prefs.get(
            'beta_mode', False))
        self.beta_checkbox.stateChanged.connect(self._set_restart_required)
        self.beta_checkbox.stateChanged.connect(self._beta_mode_changed)
        self.beta_checkbox.setToolTip(
            'Tell Calibre to communicate with the Beta version of QuietThyme')
        self.l.addWidget(self.beta_checkbox)

        self.config_url = QUrl(self.plugin_prefs.get('web_base') + '/storage')
        self.webview = QTWebView(bearer_token=self.plugin_prefs.get('token'))

        self.webview.load(self.config_url)
        self.global_settings = self.webview.page().settings(
        )  #QWebSettings.globalSettings()

        self.global_settings.setAttribute(
            QWebSettings.LocalStorageEnabled,
            True)  #required since this is where we store tokens.
        self.global_settings.setAttribute(QWebSettings.PrivateBrowsingEnabled,
                                          False)
        self.global_settings.setAttribute(QWebSettings.JavascriptEnabled, True)
        self.global_settings.setAttribute(
            QWebSettings.JavascriptCanOpenWindows, True)
        self.global_settings.setAttribute(
            QWebSettings.JavascriptCanCloseWindows, True)
        self.global_settings.setAttribute(QWebSettings.PluginsEnabled, True)
        self.global_settings.setAttribute(
            QWebSettings.LocalContentCanAccessRemoteUrls, True)
        self.global_settings.setAttribute(
            QWebSettings.LocalContentCanAccessFileUrls, True)
        self.global_settings.setAttribute(QWebSettings.XSSAuditingEnabled,
                                          False)
        self.global_settings.setAttribute(QWebSettings.DeveloperExtrasEnabled,
                                          True)

        # ensure that we get a random offline/localstorage/cache path for the brower (so that the localstorage data is not persistent across sessions)
        path = None
        while True:
            potential_path = QTemporaryDir()
            if potential_path.isValid():
                path = potential_path.path()
                break

        self.global_settings.setOfflineStoragePath(path)
        self.global_settings.setOfflineWebApplicationCachePath(path)
        self.global_settings.enablePersistentStorage(path)
        self.global_settings.setLocalStoragePath(path)

        self.webview.show()
        self.l.addWidget(self.webview)

        self.configure_webview_inspector_ui()

        self.webview.urlChanged.connect(self._url_changed)
        self.webview.loadFinished.connect(self._load_finished)

    ################################################################################################################
    # Configure UI methods
    #
    def configure_webview_inspector_ui(self):
        if self.plugin_prefs.get('inspector_enabled'):
            self.webview.setMinimumSize(QSize(600, 300))
            self.inspector = QWebInspector()
            self.inspector.setMinimumSize(QSize(600, 300))
            self.inspector.setSizePolicy(QSizePolicy.Expanding,
                                         QSizePolicy.Expanding)
            self.l.addWidget(self.inspector)
            self.inspector.setPage(self.webview.page())
        else:
            self.webview.setMinimumSize(QSize(600, 600))
            if self.inspector is not None:
                self.l.removeWidget(self.inspector)
                self.inspector.setParent(None)
                self.inspector = None

    def save_settings(self):
        self.plugin_prefs.set('inspector_enabled',
                              self.inspector_checkbox.isChecked())
        self.plugin_prefs.set('beta_mode', self.beta_checkbox.isChecked())

        # # If restart needed, inform user
        if self.restart_required:
            # do_restart = show_restart_warning('Restart calibre for the changes to be applied.',
            #                                   parent=self.l)
            # if do_restart:
            #     self.gui.quit(restart=True)

            # TODO: figure out if theres a way to call self.gui.quit()

            msg = QMessageBox()
            msg.setIcon(QMessageBox.Warning)

            msg.setText("Restart Required")
            msg.setInformativeText(
                "A configuration change requires you to restart Calibre, You should do so now,"
            )
            msg.setWindowTitle("Restart Required")
            msg.setStandardButtons(QMessageBox.Ok)
            msg.exec_()

    def validate(self):
        # TODO: validate the that the api_endpoint and web endpoint are valid urls.
        return True

    ################################################################################################################
    # Event Handlers
    #
    def _url_changed(self, url):
        logger.debug(sys._getframe().f_code.co_name, url)

    def _load_finished(self, ok):
        logger.debug(sys._getframe().f_code.co_name, ok)
        if self.webview.page().mainFrame().url() == self.config_url:
            logger.debug(
                "Loading finished and the url is the same as the desired destination url, ",
                self.config_url)
            self.webview.page().mainFrame().evaluateJavaScript("""
            console.log("GET LOCALSTORAGE TOKEN")
            """)
            token = self.webview.page().mainFrame().evaluateJavaScript("""
            localStorage.getItem('id_token');
            """)
            logger.debug("Got JWT Token", token)
            self.plugin_prefs.set('token', token)

    def _set_restart_required(self, state):
        '''
        Set restart_required flag to show show dialog when closing dialog
        '''

        logger.debug(sys._getframe().f_code.co_name, "restart required")
        self.restart_required = True

    def _beta_mode_changed(self, state):
        logger.debug(sys._getframe().f_code.co_name,
                     self.beta_checkbox.isChecked())
        self.plugin_prefs.set('beta_mode', self.beta_checkbox.isChecked())

        # if the beta mode has changed, we need to reset the api endpoints, and then wipe the token (not valid between envs)
        self.plugin_prefs.set('token', '')
        if self.plugin_prefs.get('beta_mode'):
            self.plugin_prefs.set('api_base', beta_api_base)
            self.plugin_prefs.set('web_base', beta_web_base)
        else:
            self.plugin_prefs.set('api_base', master_api_base)
            self.plugin_prefs.set('web_base', master_web_base)

        # after we reset the token, we need to generate a new QTWebView with the new config, and then reload the login page.
        self.config_url = QUrl(self.plugin_prefs.get('web_base') + '/storage')

        self.webview.set_bearer_token(self.plugin_prefs.get('token'))
        self.webview.load(self.config_url)

    def _inspector_enabled_changed(self, state):
        logger.debug(sys._getframe().f_code.co_name,
                     self.inspector_checkbox.isChecked())
        self.plugin_prefs.set('inspector_enabled',
                              self.inspector_checkbox.isChecked())
        self.configure_webview_inspector_ui()
Ejemplo n.º 55
0
class Editor(QWidget):  # {{{

    toolbar_prefs_name = None
    data_changed = pyqtSignal()

    def __init__(self,
                 parent=None,
                 one_line_toolbar=False,
                 toolbar_prefs_name=None):
        QWidget.__init__(self, parent)
        self.toolbar_prefs_name = toolbar_prefs_name or self.toolbar_prefs_name
        self.toolbar1 = QToolBar(self)
        self.toolbar2 = QToolBar(self)
        self.toolbar3 = QToolBar(self)
        for i in range(1, 4):
            t = getattr(self, 'toolbar%d' % i)
            t.setIconSize(QSize(18, 18))
        self.editor = EditorWidget(self)
        self.editor.data_changed.connect(self.data_changed)
        self.set_base_url = self.editor.set_base_url
        self.set_html = self.editor.set_html
        self.tabs = QTabWidget(self)
        self.tabs.setTabPosition(self.tabs.South)
        self.wyswyg = QWidget(self.tabs)
        self.code_edit = QPlainTextEdit(self.tabs)
        self.source_dirty = False
        self.wyswyg_dirty = True

        self._layout = QVBoxLayout(self)
        self.wyswyg.layout = l = QVBoxLayout(self.wyswyg)
        self.setLayout(self._layout)
        l.setContentsMargins(0, 0, 0, 0)
        if one_line_toolbar:
            tb = QHBoxLayout()
            l.addLayout(tb)
        else:
            tb = l
        tb.addWidget(self.toolbar1)
        tb.addWidget(self.toolbar2)
        tb.addWidget(self.toolbar3)
        l.addWidget(self.editor)
        self._layout.addWidget(self.tabs)
        self.tabs.addTab(self.wyswyg, _('N&ormal view'))
        self.tabs.addTab(self.code_edit, _('&HTML source'))
        self.tabs.currentChanged[int].connect(self.change_tab)
        self.highlighter = Highlighter(self.code_edit.document())
        self.layout().setContentsMargins(0, 0, 0, 0)
        if self.toolbar_prefs_name is not None:
            hidden = gprefs.get(self.toolbar_prefs_name)
            if hidden:
                self.hide_toolbars()

        # toolbar1 {{{
        self.toolbar1.addAction(self.editor.action_undo)
        self.toolbar1.addAction(self.editor.action_redo)
        self.toolbar1.addAction(self.editor.action_select_all)
        self.toolbar1.addAction(self.editor.action_remove_format)
        self.toolbar1.addAction(self.editor.action_clear)
        self.toolbar1.addSeparator()

        for x in ('copy', 'cut', 'paste'):
            ac = getattr(self.editor, 'action_' + x)
            self.toolbar1.addAction(ac)

        self.toolbar1.addSeparator()
        self.toolbar1.addAction(self.editor.action_background)
        # }}}

        # toolbar2 {{{
        for x in ('', 'un'):
            ac = getattr(self.editor, 'action_%sordered_list' % x)
            self.toolbar2.addAction(ac)
        self.toolbar2.addSeparator()
        for x in ('superscript', 'subscript', 'indent', 'outdent'):
            self.toolbar2.addAction(getattr(self.editor, 'action_' + x))
            if x in ('subscript', 'outdent'):
                self.toolbar2.addSeparator()

        self.toolbar2.addAction(self.editor.action_block_style)
        w = self.toolbar2.widgetForAction(self.editor.action_block_style)
        if hasattr(w, 'setPopupMode'):
            w.setPopupMode(w.InstantPopup)
        self.toolbar2.addAction(self.editor.action_insert_link)
        self.toolbar2.addAction(self.editor.action_insert_hr)
        # }}}

        # toolbar3 {{{
        for x in ('bold', 'italic', 'underline', 'strikethrough'):
            ac = getattr(self.editor, 'action_' + x)
            self.toolbar3.addAction(ac)
        self.toolbar3.addSeparator()

        for x in ('left', 'center', 'right', 'justified'):
            ac = getattr(self.editor, 'action_align_' + x)
            self.toolbar3.addAction(ac)
        self.toolbar3.addSeparator()
        self.toolbar3.addAction(self.editor.action_color)
        # }}}

        self.code_edit.textChanged.connect(self.code_dirtied)
        self.editor.data_changed.connect(self.wyswyg_dirtied)

    def set_minimum_height_for_editor(self, val):
        self.editor.setMinimumHeight(val)

    @property
    def html(self):
        self.tabs.setCurrentIndex(0)
        return self.editor.html

    @html.setter
    def html(self, v):
        self.editor.html = v

    def change_tab(self, index):
        # print 'reloading:', (index and self.wyswyg_dirty) or (not index and
        #        self.source_dirty)
        if index == 1:  # changing to code view
            if self.wyswyg_dirty:
                self.code_edit.setPlainText(self.editor.html)
                self.wyswyg_dirty = False
        elif index == 0:  # changing to wyswyg
            if self.source_dirty:
                self.editor.html = unicode_type(self.code_edit.toPlainText())
                self.source_dirty = False

    @property
    def tab(self):
        return 'code' if self.tabs.currentWidget(
        ) is self.code_edit else 'wyswyg'

    @tab.setter
    def tab(self, val):
        self.tabs.setCurrentWidget(self.code_edit if val ==
                                   'code' else self.wyswyg)

    def wyswyg_dirtied(self, *args):
        self.wyswyg_dirty = True

    def code_dirtied(self, *args):
        self.source_dirty = True

    def hide_toolbars(self):
        self.toolbar1.setVisible(False)
        self.toolbar2.setVisible(False)
        self.toolbar3.setVisible(False)

    def show_toolbars(self):
        self.toolbar1.setVisible(True)
        self.toolbar2.setVisible(True)
        self.toolbar3.setVisible(True)

    def toggle_toolbars(self):
        visible = self.toolbars_visible
        getattr(self, ('hide' if visible else 'show') + '_toolbars')()
        if self.toolbar_prefs_name is not None:
            gprefs.set(self.toolbar_prefs_name, visible)

    @property
    def toolbars_visible(self):
        return self.toolbar1.isVisible() or self.toolbar2.isVisible(
        ) or self.toolbar3.isVisible()

    @toolbars_visible.setter
    def toolbars_visible(self, val):
        getattr(self, ('show' if val else 'hide') + '_toolbars')()

    def set_readonly(self, what):
        self.editor.set_readonly(what)

    def hide_tabs(self):
        self.tabs.tabBar().setVisible(False)
Ejemplo n.º 56
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 PyQt5.Qt 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.Ok | QDialogButtonBox.Cancel)
        v = QVBoxLayout(config_dialog)

        def size_dialog():
            if geom is None:
                config_dialog.resize(config_dialog.sizeHint())
            else:
                config_dialog.restoreGeometry(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.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.LinksAccessibleByMouse | Qt.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.Accepted:
                sc = unicode_type(sc.text()).strip()
                customize_plugin(self, sc)

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

        return config_dialog.result()
Ejemplo n.º 57
0
class Configure(Dialog):
    def __init__(self, db, parent=None):
        self.db = db
        Dialog.__init__(self, _('Configure the Book details window'),
                        'book-details-popup-conf', parent)

    def setup_ui(self):
        from calibre.gui2.preferences.look_feel import DisplayedFields, move_field_up, move_field_down
        self.l = QVBoxLayout(self)
        self.field_display_order = fdo = QListView(self)
        self.model = DisplayedFields(self.db,
                                     fdo,
                                     pref_name='popup_book_display_fields')
        self.model.initialize()
        fdo.setModel(self.model)
        fdo.setAlternatingRowColors(True)
        del self.db
        self.l.addWidget(QLabel(_('Select displayed metadata')))
        h = QHBoxLayout()
        h.addWidget(fdo)
        v = QVBoxLayout()
        self.mub = b = QToolButton(self)
        connect_lambda(b.clicked, self,
                       lambda self: move_field_up(fdo, self.model))
        b.setIcon(QIcon(I('arrow-up.png')))
        b.setToolTip(_('Move the selected field up'))
        v.addWidget(b), v.addStretch(10)
        self.mud = b = QToolButton(self)
        b.setIcon(QIcon(I('arrow-down.png')))
        b.setToolTip(_('Move the selected field down'))
        connect_lambda(b.clicked, self,
                       lambda self: move_field_down(fdo, self.model))
        v.addWidget(b)
        h.addLayout(v)

        self.l.addLayout(h)
        self.l.addWidget(
            QLabel('<p>' + _(
                'Note that <b>comments</b> will always be displayed at the end, regardless of the order you assign here'
            )))

        b = self.bb.addButton(_('Restore &defaults'), self.bb.ActionRole)
        b.clicked.connect(self.restore_defaults)
        b = self.bb.addButton(_('Select &all'), self.bb.ActionRole)
        b.clicked.connect(self.select_all)
        b = self.bb.addButton(_('Select &none'), self.bb.ActionRole)
        b.clicked.connect(self.select_none)
        self.l.addWidget(self.bb)
        self.setMinimumHeight(500)

    def select_all(self):
        self.model.toggle_all(True)

    def select_none(self):
        self.model.toggle_all(False)

    def restore_defaults(self):
        self.model.initialize(use_defaults=True)

    def accept(self):
        self.model.commit()
        return Dialog.accept(self)
Ejemplo n.º 58
0
    def setup_ui(self):

        # Create layout for entire dialog
        layout = QVBoxLayout(self)
        self.setLayout(layout)

        #Create a scroll area for the top part of the dialog
        self.scrollArea = QScrollArea(self)
        self.scrollArea.setWidgetResizable(True)

        # Create widget for all the contents of the dialog except the OK and Cancel buttons
        self.scrollContentWidget = QWidget(self.scrollArea)
        self.scrollArea.setWidget(self.scrollContentWidget)
        widgetLayout = QVBoxLayout(self.scrollContentWidget)

        # Add scrollArea to dialog
        layout.addWidget(self.scrollArea)

        self.other_group_box = QGroupBox(_('Other Changes'))
        widgetLayout.addWidget(self.other_group_box)
        other_group_box_layout = QVBoxLayout()
        self.other_group_box.setLayout(other_group_box_layout)

        text_dir_layout = QHBoxLayout()
        other_group_box_layout.addLayout(text_dir_layout)
        direction_label = QLabel(_('Text Direction:'))
        text_dir_layout.addWidget(direction_label)
        self.text_dir_combo = QComboBox()
        text_dir_layout.addWidget(self.text_dir_combo)
        self.text_dir_combo.addItems([_('No Conversion'), _('Horizontal'), _('Vertical')])
        self.text_dir_combo.setToolTip(_('Select the desired text orientation'))
        self.text_dir_combo.currentIndexChanged.connect(self.update_gui)


        self.optimization_group_box = QGroupBox(_('Reader Device Optimization'))
        other_group_box_layout.addWidget(self.optimization_group_box)
        optimization_group_box_layout = QVBoxLayout()
        self.optimization_group_box.setLayout(optimization_group_box_layout)
        
        punc_group=QButtonGroup(self)
        self.text_dir_punc_none_button = QRadioButton("""No presentation optimization""")
        optimization_group_box_layout.addWidget(self.text_dir_punc_none_button)
        self.text_dir_punc_button = QRadioButton("""Optimize presentation for Readium reader""")
        self.text_dir_punc_button.setToolTip(_('Use vert/horiz punctuation presentation forms for Chrome Readium Epub3 reader'))
        optimization_group_box_layout.addWidget(self.text_dir_punc_button)
        self.text_dir_punc_kindle_button = QRadioButton("""Optimize presentation for Kindle reader""")
        self.text_dir_punc_kindle_button.setToolTip(_('Use vert/horiz puncuation presentation forms for Kindle reader'))
        optimization_group_box_layout.addWidget(self.text_dir_punc_kindle_button)
        self.text_dir_punc_none_button.toggled.connect(self.update_gui)
        self.text_dir_punc_button.toggled.connect(self.update_gui)
        self.text_dir_punc_kindle_button.toggled.connect(self.update_gui)

        source_group=QButtonGroup(self)
        self.file_source_button = QRadioButton(_('Selected File Only'))
        self.book_source_button = QRadioButton(_('Entire eBook'))
        source_group.addButton(self.file_source_button)
        source_group.addButton(self.book_source_button)
        self.source_group_box = QGroupBox(_('Source'))
        if not self.force_entire_book:
            widgetLayout.addWidget(self.source_group_box)
            source_group_box_layout = QVBoxLayout()
            self.source_group_box.setLayout(source_group_box_layout)
            source_group_box_layout.addWidget(self.file_source_button)
            source_group_box_layout.addWidget(self.book_source_button)

        layout.addSpacing(10)
        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)

        self.button_box.accepted.connect(self._ok_clicked)
        self.button_box.rejected.connect(self.reject)
        layout.addWidget(self.button_box)

        if not self.force_entire_book:
            self.file_source_button.setChecked(self.prefs['use_html_file'])
            self.book_source_button.setChecked(self.prefs['use_entire_book'])
        else:
            self.file_source_button.setChecked(False)
            self.book_source_button.setChecked(True)

        self.text_dir_combo.setCurrentIndex(self.prefs['orientation'])
        self.text_dir_punc_none_button.setChecked(self.prefs['no_optimization'])
        self.text_dir_punc_button.setChecked(self.prefs['readium_optimization'])
        self.text_dir_punc_kindle_button.setChecked(self.prefs['kindle_optimization'])
        self.update_gui()
Ejemplo n.º 59
0
class TagBrowserWidget(QWidget):  # {{{

    def __init__(self, parent):
        QWidget.__init__(self, parent)
        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)
        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().returnPressed.connect(self.do_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.addWidget(parent.tags_view)

        # Now the floating 'not found' box
        l = QLabel(self.tags_view)
        self.not_found_label = l
        l.setFrameStyle(QFrame.StyledPanel)
        l.setAutoFillBackground(True)
        l.setText('<p><b>'+_('No More Matches.</b><p> Click Find again to go to first match'))
        l.setAlignment(Qt.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.QueuedConnection)
        # The Alter Tag Browser button
        l = self.alter_tb
        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'))
        ac.triggered.connect(lambda : self.tags_view.collapseAll())

        ac = QAction(parent)
        parent.addAction(ac)
        parent.keyboard.register_shortcut('tag browser alter',
                _('Alter Tag browser'), default_keys=(),
                action=ac, group=_('Tag browser'))
        ac.triggered.connect(l.showMenu)

        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)

        # self.leak_test_timer = QTimer(self)
        # self.leak_test_timer.timeout.connect(self.test_for_leak)
        # self.leak_test_timer.start(5000)

    def save_state(self):
        gprefs.set('tag browser search box visible', self.toggle_search_button.isChecked())

    def toggle_item(self):
        self.tags_view.toggle_current_index()

    def set_pane_is_visible(self, to_what):
        self.tags_view.set_pane_is_visible(to_what)

    def find_text_changed(self, str):
        self.current_find_position = None

    def set_focus_to_find_box(self):
        self.tb_bar.set_focus_to_find_box()

    def do_find(self, str=None):
        self.current_find_position = None
        self.find()

    def find(self):
        model = self.tags_view.model()
        model.clear_boxed()
        txt = unicode(self.item_search.currentText()).strip()

        if txt.startswith('*'):
            model.set_categories_filter(txt[1:])
            self.tags_view.recount()
            self.current_find_position = None
            return
        if model.get_categories_filter():
            model.set_categories_filter(None)
            self.tags_view.recount()
            self.current_find_position = None

        if not txt:
            return

        self.item_search.lineEdit().blockSignals(True)
        self.search_button.setFocus(True)
        self.item_search.lineEdit().blockSignals(False)

        key = None
        colon = txt.rfind(':') if len(txt) > 2 else 0
        if colon > 0:
            key = self._parent.library_view.model().db.\
                        field_metadata.search_term_to_field_key(txt[:colon])
            txt = txt[colon+1:]

        self.current_find_position = \
            model.find_item_node(key, txt, self.current_find_position)

        if self.current_find_position:
            self.tags_view.show_item_at_path(self.current_find_position, box=True)
        elif self.item_search.text():
            self.not_found_label.setVisible(True)
            if self.tags_view.verticalScrollBar().isVisible():
                sbw = self.tags_view.verticalScrollBar().width()
            else:
                sbw = 0
            width = self.width() - 8 - sbw
            height = self.not_found_label.heightForWidth(width) + 20
            self.not_found_label.resize(width, height)
            self.not_found_label.move(4, 10)
            self.not_found_label_timer.start(2000)

    def not_found_label_timer_event(self):
        self.not_found_label.setVisible(False)
Ejemplo n.º 60
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.Cancel, Qt.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