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())
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())
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()
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()
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)
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)
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()
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()
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)
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())
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)
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'))
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)
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()
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)
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
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
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()
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
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())
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()
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)
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)
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)
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)
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()
def _setup_ui(self): layout = QVBoxLayout() layout.addWidget(self._label) self.setLayout(layout)
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)
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))
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()
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()
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
#!/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()
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
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)
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
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()
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())
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'])
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))
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)
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
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()
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)
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()
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()]
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()
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()
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)
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()
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)
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()
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)
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