def initialize(self, catalog_name, db): self.name = catalog_name from calibre.library.catalogs import FIELDS db = get_gui().current_db self.all_fields = {x for x in FIELDS if x != 'all'} | set(db.custom_field_keys()) sort_order, fields = get_saved_field_data(self.name, self.all_fields) fm = db.field_metadata def name(x): if x == 'isbn': return 'ISBN' if x == 'library_name': return _('Library name') if x.endswith('_index'): return name(x[:-len('_index')]) + ' ' + _('Number') return fm[x].get('name') or x def key(x): return (sort_order.get(x, 10000), name(x)) self.db_fields.clear() for x in sorted(self.all_fields, key=key): QListWidgetItem(name(x) + ' (%s)' % x, self.db_fields).setData(Qt.ItemDataRole.UserRole, x) if x.startswith('#') and fm[x]['datatype'] == 'series': x += '_index' QListWidgetItem(name(x) + ' (%s)' % x, self.db_fields).setData(Qt.ItemDataRole.UserRole, x) # Restore the activated fields from last use for x in range(self.db_fields.count()): item = self.db_fields.item(x) item.setCheckState(Qt.CheckState.Checked if str(item.data(Qt.ItemDataRole.UserRole)) in fields else Qt.CheckState.Unchecked)
def setup_export_panel(self): self.export_panel = w = QWidget(self) self.stack.addWidget(w) w.l = l = QVBoxLayout(w) w.la = la = QLabel( _('Select which libraries you want to export below')) la.setWordWrap(True), l.addWidget(la) self.lib_list = ll = QListWidget(self) l.addWidget(ll) ll.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection) ll.setStyleSheet('QListView::item { padding: 5px }') ll.setAlternatingRowColors(True) lpaths = all_known_libraries() for lpath in sorted( lpaths, key=lambda x: numeric_sort_key(os.path.basename(x))): i = QListWidgetItem(self.export_lib_text(lpath), ll) i.setData(Qt.ItemDataRole.UserRole, lpath) i.setData(Qt.ItemDataRole.UserRole + 1, lpaths[lpath]) i.setIcon(QIcon(I('lt.png'))) i.setSelected(True) self.update_disk_usage.connect( (lambda i, sz: self.lib_list.item(i).setText( self.export_lib_text( self.lib_list.item(i).data(Qt.ItemDataRole.UserRole), sz)) ), type=Qt.ConnectionType.QueuedConnection)
def _apply_prefs(self, prefs): for x in ('title', 'subtitle', 'footer'): attr = '%s_font_family' % x getattr(self, attr).font_family = prefs[attr] attr = '%s_font_size' % x getattr(self, attr).setValue(prefs[attr]) for x in ('title', 'subtitle', 'footer'): x += '_template' getattr(self, x).setText(prefs[x]) for x in ('width', 'height'): x = 'cover_' + x getattr(self, x).setValue(prefs[x]) color_themes = prefs['color_themes'].copy() color_themes.update(default_color_themes) disabled = set(prefs['disabled_color_themes']) self.colors_list.clear() self.colors_map = {} for name in sorted(color_themes, key=sort_key): self.colors_map[name] = li = QListWidgetItem( name, self.colors_list) li.setFlags(li.flags() | Qt.ItemFlag.ItemIsUserCheckable) li.setCheckState(Qt.CheckState.Unchecked if name in disabled else Qt.CheckState.Checked) li.setData(Qt.ItemDataRole.UserRole, color_themes[name]) lu = prefs.get('last_used_colors') if not self.for_global_prefs and lu in self.colors_map and self.colors_map[ lu].checkState() == Qt.CheckState.Checked: self.colors_map[lu].setSelected(True) else: for name, li in iteritems(self.colors_map): if li.checkState() == Qt.CheckState.Checked: li.setSelected(True) break else: next(itervalues(self.colors_map)).setSelected(True) disabled = set(prefs['disabled_styles']) self.styles_list.clear() self.style_map.clear() for name in sorted(all_styles(), key=sort_key): self.style_map[name] = li = QListWidgetItem(name, self.styles_list) li.setFlags(li.flags() | Qt.ItemFlag.ItemIsUserCheckable) li.setCheckState(Qt.CheckState.Unchecked if name in disabled else Qt.CheckState.Checked) lu = prefs.get('last_used_style') if not self.for_global_prefs and lu in self.style_map and self.style_map[ lu].checkState() == Qt.CheckState.Checked: self.style_map[lu].setSelected(True) else: for name, li in iteritems(self.style_map): if li.checkState() == Qt.CheckState.Checked: li.setSelected(True) break else: next(itervalues(self.style_map)).setSelected(True)
def build_lists(self): from calibre.gui2.tweak_book.plugin import plugin_toolbar_actions self.available.clear(), self.current.clear() name = self.current_name if not name: return items = self.current_settings[name] applied = set(items) if name == 'global_plugins_toolbar': all_items = {x.sid: x for x in plugin_toolbar_actions} elif name.startswith('global_'): all_items = toolbar_actions elif name == 'editor_common_toolbar': all_items = {x: actions[x] for x in tprefs.defaults[name] if x} else: all_items = editor_toolbar_actions[name.split('_')[1]] blank = QIcon(I('blank.png')) def to_item(key, ac, parent): ic = ac.icon() if not ic or ic.isNull(): ic = blank ans = QListWidgetItem(ic, unicode_type(ac.text()).replace('&', ''), parent) ans.setData(Qt.ItemDataRole.UserRole, key) ans.setToolTip(ac.toolTip()) return ans for key, ac in sorted(iteritems(all_items), key=lambda k_ac: unicode_type(k_ac[1].text())): if key not in applied: to_item(key, ac, self.available) if name == 'global_book_toolbar' and 'donate' not in applied: QListWidgetItem(QIcon(I('donate.png')), _('Donate'), self.available).setData(Qt.ItemDataRole.UserRole, 'donate') QListWidgetItem(blank, '--- %s ---' % _('Separator'), self.available) for key in items: if key is None: QListWidgetItem(blank, '--- %s ---' % _('Separator'), self.current) else: if key == 'donate': QListWidgetItem(QIcon(I('donate.png')), _('Donate'), self.current).setData( Qt.ItemDataRole.UserRole, 'donate') else: try: ac = all_items[key] except KeyError: pass else: to_item(key, ac, self.current)
def resort(self): if self.sort_alphabetically.isChecked(): sorted_locations = sorted( self.locations, key=lambda name_loc: numeric_sort_key(name_loc[0])) else: sorted_locations = self.locations self.items.clear() for name, loc in sorted_locations: i = QListWidgetItem(name, self.items) i.setData(Qt.ItemDataRole.UserRole, loc) self.items.setCurrentRow(0)
def setup_ui(self): self.vl = vl = QVBoxLayout(self) self.la = la = QLabel( _('Pick multiple columns to sort by. Drag and drop to re-arrange. Higher columns are more important.' ' Ascending or descending order can be toggled by clicking the column name at the bottom' ' of this dialog, after having selected it.')) la.setWordWrap(True) vl.addWidget(la) self.order_label = la = QLabel('\xa0') la.setTextFormat(Qt.TextFormat.RichText) la.setWordWrap(True) la.linkActivated.connect(self.link_activated) self.column_list = cl = QListWidget(self) vl.addWidget(cl) vl.addWidget(la) vl.addWidget(self.bb) for name in self.all_names: i = QListWidgetItem(cl) i.setText(name) i.setData(Qt.ItemDataRole.UserRole, self.name_map[name]) cl.addItem(i) i.setCheckState(Qt.CheckState.Unchecked) if self.name_map[name] in self.hidden_fields: i.setHidden(True) cl.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove) cl.currentRowChanged.connect(self.current_changed) cl.itemDoubleClicked.connect(self.item_double_clicked) cl.setCurrentRow(0) cl.itemChanged.connect(self.update_order_label) cl.model().rowsMoved.connect(self.update_order_label) self.clear_button = b = self.bb.addButton( _('&Clear'), QDialogButtonBox.ButtonRole.ActionRole) b.setToolTip(_('Clear all selected columns')) b.setAutoDefault(False) b.clicked.connect(self.clear) self.save_button = b = self.bb.addButton( _('&Save'), QDialogButtonBox.ButtonRole.ActionRole) b.setToolTip(_('Save this sort order for easy re-use')) b.clicked.connect(self.save) b.setAutoDefault(False) self.load_button = b = self.bb.addButton( _('&Load'), QDialogButtonBox.ButtonRole.ActionRole) b.setToolTip(_('Load previously saved settings')) b.setAutoDefault(False) self.load_menu = QMenu(b) b.setMenu(self.load_menu) self.load_menu.aboutToShow.connect(self.populate_load_menu)
def __init__(self, all_formats, format_map): QWidget.__init__(self) self.l = l = QGridLayout() self.setLayout(l) self.f = f = QListWidget(self) l.addWidget(f, 0, 0, 3, 1) unchecked_formats = sorted(all_formats - set(format_map)) for fmt in format_map + unchecked_formats: item = QListWidgetItem(fmt, f) item.setData(Qt.ItemDataRole.UserRole, fmt) item.setFlags(Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsUserCheckable | Qt.ItemFlag.ItemIsSelectable) item.setCheckState(Qt.CheckState.Checked if fmt in format_map else Qt.CheckState.Unchecked) self.button_up = b = QToolButton(self) b.setIcon(QIcon(I('arrow-up.png'))) l.addWidget(b, 0, 1) b.clicked.connect(self.up) self.button_down = b = QToolButton(self) b.setIcon(QIcon(I('arrow-down.png'))) l.addWidget(b, 2, 1) b.clicked.connect(self.down)
def create_item(self, alias, key, checked=False): i = QListWidgetItem(alias, self.recipients) i.setFlags(Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsUserCheckable) i.setCheckState( Qt.CheckState.Checked if checked else Qt.CheckState.Unchecked) i.setData(Qt.ItemDataRole.UserRole, key) self.items.append(i)
def setup_ui(self): from calibre.ebooks.oeb.polish.images import get_compressible_images self.setWindowIcon(QIcon(I('compress-image.png'))) self.h = h = QHBoxLayout(self) self.images = i = QListWidget(self) h.addWidget(i) self.l = l = QVBoxLayout() h.addLayout(l) c = current_container() for name in sorted(get_compressible_images(c), key=numeric_sort_key): x = QListWidgetItem(name, i) x.setData(Qt.ItemDataRole.UserRole, c.filesize(name)) i.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection) i.setMinimumHeight(350), i.setMinimumWidth(350) i.selectAll(), i.setSpacing(5) self.delegate = ImageItemDelegate(self) i.setItemDelegate(self.delegate) self.la = la = QLabel( _('You can compress the images in this book losslessly, reducing the file size of the book,' ' without affecting image quality. Typically image size is reduced by 5 - 15%.' )) la.setWordWrap(True) la.setMinimumWidth(250) l.addWidget(la), l.addSpacing(30) self.enable_lossy = el = QCheckBox( _('Enable &lossy compression of JPEG images')) el.setToolTip( _('This allows you to change the quality factor used for JPEG images.\nBy lowering' ' the quality you can greatly reduce file size, at the expense of the image looking blurred.' )) l.addWidget(el) self.h2 = h = QHBoxLayout() l.addLayout(h) self.jq = jq = QSpinBox(self) jq.setMinimum(0), jq.setMaximum(100), jq.setValue( tprefs.get('jpeg_compression_quality_for_lossless_compression', 80)), jq.setEnabled(False) jq.setToolTip( _('The compression quality, 1 is high compression, 100 is low compression.\nImage' ' quality is inversely correlated with compression quality.')) jq.valueChanged.connect(self.save_compression_quality) el.toggled.connect(jq.setEnabled) self.jql = la = QLabel(_('Compression &quality:')) la.setBuddy(jq) h.addWidget(la), h.addWidget(jq) l.addStretch(10) l.addWidget(self.bb)
def initialize(self, name, db): # not working properly to update from calibre.library.catalogs import FIELDS self.all_fields = [x for x in FIELDS if x != 'all'] # add custom columns for x in sorted(db.custom_field_keys()): self.all_fields.append(x) if db.field_metadata[x]['datatype'] == 'series': self.all_fields.append(x + '_index') # populate for x in self.all_fields: QListWidgetItem(x, self.db_fields) self.name = name fields = gprefs.get(name + '_db_fields', self.all_fields) # Restore the activated db_fields from last use for x in range(self.db_fields.count()): item = self.db_fields.item(x) item.setSelected(str(item.text()) in fields) self.bibfile_enc.clear() self.bibfile_enc.addItems(['utf-8', 'cp1252', 'ascii/LaTeX']) self.bibfile_enctag.clear() self.bibfile_enctag.addItems( ['strict', 'replace', 'ignore', 'backslashreplace']) self.bib_entry.clear() self.bib_entry.addItems(['mixed', 'misc', 'book']) # Update dialog fields from stored options for opt in self.OPTION_FIELDS: opt_value = gprefs.get(self.name + '_' + opt[0], opt[1]) if opt[0] in ['bibfile_enc', 'bibfile_enctag', 'bib_entry']: getattr(self, opt[0]).setCurrentIndex(opt_value) elif opt[0] in ['impcit', 'addfiles']: getattr(self, opt[0]).setChecked(opt_value) else: getattr(self, opt[0]).setText(opt_value)
def indicate_no_items(self): self.no_valid_items = True self.items.clear() self.add_columns_to_widget() self.items.addItem(QListWidgetItem(_('**No items found**'))) self.books_label.setText(_('Click in a column in the library view ' 'to see the information for that book'))
def book_converted(self, book_id, fmt): fmt = fmt.upper() if fmt not in self._formats: self._formats.append(fmt) self.formats.addItem( QListWidgetItem( file_icon_provider().icon_from_ext(fmt.lower()), fmt.upper()))
def refill_all_boxes(self): if self.refilling: return self.refilling = True self.current_device = None self.current_format = None self.clear_fields(new_boxes=True) self.edit_format.clear() self.edit_format.addItem('') for format_ in self.current_plugboards: self.edit_format.addItem(format_) self.edit_format.setCurrentIndex(0) self.edit_device.clear() self.ok_button.setEnabled(False) self.del_button.setEnabled(False) self.existing_plugboards.clear() for f in self.formats: if f not in self.current_plugboards: continue for d in sorted(self.devices + self.disabled_devices, key=lambda x: x.lower()): if d not in self.current_plugboards[f]: continue ops = [] for op in self.current_plugboards[f][d]: ops.append('([' + op[0] + '] -> ' + op[1] + ')') txt = '%s:%s = %s\n' % (f, d, ', '.join(ops)) item = QListWidgetItem(txt) item.setData(Qt.ItemDataRole.UserRole, (f, d)) if d in self.disabled_devices: item.setFlags(item.flags() & ~Qt.ItemFlag.ItemIsEnabled) self.existing_plugboards.addItem(item) self.refilling = False
def change_builtin(self): d = QDialog(self) lw = QListWidget(d) for (trigger, syntaxes), snip in iteritems(builtin_snippets): snip = copy.deepcopy(snip) snip['trigger'], snip['syntaxes'] = trigger, syntaxes i = QListWidgetItem(self.snip_to_text(snip), lw) i.setData(Qt.ItemDataRole.UserRole, snip) d.l = l = QVBoxLayout(d) l.addWidget(QLabel(_('Choose the built-in snippet to modify:'))) l.addWidget(lw) lw.itemDoubleClicked.connect(d.accept) d.bb = bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel) l.addWidget(bb) bb.accepted.connect(d.accept), bb.rejected.connect(d.reject) if d.exec_() == QDialog.DialogCode.Accepted and lw.currentItem() is not None: self.stack.setCurrentIndex(1) self.edit_snip.apply_snip(lw.currentItem().data(Qt.ItemDataRole.UserRole), creating_snippet=True)
def data(self, role): if role == Qt.ItemDataRole.DisplayRole: if self.initial_value != self.current_value: return _('%(curr)s (was %(initial)s)') % dict( curr=self.current_value, initial=self.initial_value) else: return self.current_value elif role == Qt.ItemDataRole.EditRole: return self.current_value else: return QListWidgetItem.data(self, role)
def setter(w, val): order_map = {x: i for i, x in enumerate(val)} items = list(w.defaults) limit = len(items) items.sort(key=lambda x: order_map.get(x, limit)) w.clear() for x in items: i = QListWidgetItem(w) i.setText(x) i.setFlags(i.flags() | Qt.ItemFlag.ItemIsDragEnabled)
def show_pages(self): if self.error is not None: error_dialog(self, _('Failed to render'), _('Could not render this PDF file'), show=True, det_msg=self.error) self.reject() return self.stack.stop() files = glob(os.path.join(self.current_tdir, '*.jpg')) + glob(os.path.join(self.current_tdir, '*.jpeg')) if not files and not self.covers.count(): error_dialog(self, _('Failed to render'), _('This PDF has no pages'), show=True) self.reject() return try: dpr = self.devicePixelRatioF() except AttributeError: dpr = self.devicePixelRatio() for i, f in enumerate(sorted(files)): p = QPixmap(f).scaled( self.covers.iconSize()*dpr, aspectRatioMode=Qt.AspectRatioMode.IgnoreAspectRatio, transformMode=Qt.TransformationMode.SmoothTransformation) p.setDevicePixelRatio(dpr) i = QListWidgetItem(_('page %d') % (self.first + i)) i.setData(Qt.ItemDataRole.DecorationRole, p) i.setData(Qt.ItemDataRole.UserRole, f) self.covers.addItem(i) self.first += len(files) if len(files) == PAGES_PER_RENDER: self.more_pages.setVisible(True)
def resources(self, resources): self.items.clear() self.original_resources = resources dc = 0 for url, matches in iteritems(resources): text = url num = len(matches) if text.startswith('data:'): dc += 1 text = _('Data URL #{}').format(dc) text += ' ({})'.format(ngettext('one instance', '{} instances', num).format(num)) i = QListWidgetItem(text, self.items) i.setData(Qt.ItemDataRole.UserRole, url) i.setCheckState(Qt.CheckState.Checked) i.setFlags(Qt.ItemFlag.ItemIsUserCheckable | Qt.ItemFlag.ItemIsEnabled)
def to_item(key, ac, parent): ic = ac.icon() if not ic or ic.isNull(): ic = blank ans = QListWidgetItem(ic, str(ac.text()).replace('&', ''), parent) ans.setData(Qt.ItemDataRole.UserRole, key) ans.setToolTip(ac.toolTip()) return ans
def entry_to_item(entry, parent): icon = get_icon(entry.get('icon_file'), as_data=False) if icon is None: icon = entry_to_icon_text(entry)[0] else: icon = QPixmap.fromImage(icon) ans = QListWidgetItem(QIcon(icon), entry.get('name') or _('Unknown'), parent) ans.setData(ENTRY_ROLE, entry) ans.setToolTip(_('Application path:') + '\n' + entry['path'])
def entry_to_item(entry, parent): icon_path = entry.get('Icon') or I('blank.png') if not isinstance(icon_path, string_or_bytes): icon_path = I('blank.png') ans = QListWidgetItem(QIcon(icon_path), entry.get('Name') or _('Unknown'), parent) ans.setData(ENTRY_ROLE, entry) comment = (entry.get('Comment') or '') if comment: comment += '\n' ans.setToolTip(comment + _('Command line:') + '\n' + (' '.join(entry['Exec'])))
def create_color_scheme(self): scheme = self.colors_map[self.current_colors].data(Qt.ItemDataRole.UserRole) d = CreateColorScheme('#' + _('My Color Scheme'), scheme, set(self.colors_map), parent=self) if d.exec_() == QDialog.DialogCode.Accepted: name, scheme = d.data li = QListWidgetItem(name) li.setData(Qt.ItemDataRole.UserRole, scheme), li.setFlags(li.flags() | Qt.ItemFlag.ItemIsUserCheckable), li.setCheckState(Qt.CheckState.Checked) self.insert_scheme(name, li) self.emit_changed() self.original_prefs['color_themes'] = self.current_prefs['color_themes']
def add_feed(self): title = self.feed_title.text().strip() if not title: return error_dialog(self, _('No feed title'), _('You must specify a title for the feed'), show=True) url = self.feed_url.text().strip() if not title: return error_dialog(self, _('No feed URL'), _('You must specify a URL for the feed'), show=True) QListWidgetItem(f'{title} - {url}', self.feeds).setData(Qt.ItemDataRole.UserRole, (title, url)) self.feed_title.clear(), self.feed_url.clear()
def entry_to_item(entry, parent): try: icon = icon_for_entry(entry) except Exception: icon = None import traceback traceback.print_exc() if not icon: icon = entry_to_icon_text(entry)[0] ans = QListWidgetItem(QIcon(icon), entry.get('name') or _('Unknown'), parent) ans.setData(ENTRY_ROLE, entry) ans.setToolTip(_('Command line:') + '\n' + entry['cmdline'])
def add_item_from_name(self, action): aa = all_actions() if action is None: i = QListWidgetItem('-- {} --'.format(_('Separator')), self) else: try: a = getattr(aa, action) except AttributeError: return i = QListWidgetItem(a.icon, a.text, self) i.setData(Qt.ItemDataRole.UserRole, action) return i
def recipe_source(self, src): self.feeds.clear() self.feed_title.clear() self.feed_url.clear() if src is None: self.title.setText(_('My news source')) self.oldest_article.setValue(7) self.max_articles.setValue(100) else: recipe = compile_recipe(src) self.title.setText(recipe.title) self.oldest_article.setValue(recipe.oldest_article) self.max_articles.setValue(recipe.max_articles_per_feed) for x in (recipe.feeds or ()): title, url = ('', x) if len(x) == 1 else x QListWidgetItem(f'{title} - {url}', self.feeds).setData(Qt.ItemDataRole.UserRole, (title, url))
def init_input_order(self, defaults=False): if defaults: input_map = prefs.defaults['input_format_order'] else: input_map = prefs['input_format_order'] all_formats = set() self.opt_input_order.clear() for fmt in all_input_formats().union({'ZIP', 'RAR'}): all_formats.add(fmt.upper()) for format in input_map + list(all_formats.difference(input_map)): item = QListWidgetItem(format, self.opt_input_order) item.setData(Qt.ItemDataRole.UserRole, (format)) item.setFlags(Qt.ItemFlag.ItemIsEnabled|Qt.ItemFlag.ItemIsSelectable|Qt.ItemFlag.ItemIsDragEnabled)
def __init__(self, devs, blacklist): QWidget.__init__(self) self.l = l = QVBoxLayout() self.setLayout(l) self.la = la = QLabel('<p>'+_( '''Select the devices to be <b>ignored</b>. calibre <b>will not</b> connect to devices with a checkmark next to their names.''')) la.setWordWrap(True) l.addWidget(la) self.f = f = QListWidget(self) l.addWidget(f) devs = [(snum, (x[0], parse_date(x[1]))) for snum, x in iteritems(devs)] for dev, x in sorted(devs, key=lambda x:x[1][1], reverse=True): name = x[0] name = '%s [%s]'%(name, dev) item = QListWidgetItem(name, f) item.setData(Qt.ItemDataRole.UserRole, dev) item.setFlags(Qt.ItemFlag.ItemIsEnabled|Qt.ItemFlag.ItemIsUserCheckable|Qt.ItemFlag.ItemIsSelectable) item.setCheckState(Qt.CheckState.Checked if dev in blacklist else Qt.CheckState.Unchecked)
def apply_report(self): theme = self.report.theme self.title.setText((theme.title or '').strip()) self.author.setText((theme.author or '').strip()) self.version.setValue(theme.version or 1) self.description.setText((theme.description or '').strip()) self.license.setText((theme.license or 'Unknown').strip()) self.url.setText((theme.url or '').strip()) if self.report.missing: title = _('%d icons missing in this theme') % len( self.report.missing) else: title = _('No missing icons') self.missing_icons_group.setTitle(title) mi = self.mising_icons mi.clear() for name in sorted(self.report.missing): QListWidgetItem(QIcon(I(name, allow_user_override=False)), name, mi)
def __init__(self, parent, duplicates, loc): QDialog.__init__(self, parent) l = QVBoxLayout() self.setLayout(l) self.la = la = QLabel( _('Books with the same, title, author and language as the following already exist in the library %s.' ' Select which books you want copied anyway.') % os.path.basename(loc)) la.setWordWrap(True) l.addWidget(la) self.setWindowTitle(_('Duplicate books')) self.books = QListWidget(self) self.items = [] for book_id, (title, authors) in iteritems(duplicates): i = QListWidgetItem( _('{0} by {1}').format(title, ' & '.join(authors[:3])), self.books) i.setData(Qt.ItemDataRole.UserRole, book_id) i.setFlags(Qt.ItemFlag.ItemIsUserCheckable | Qt.ItemFlag.ItemIsEnabled) i.setCheckState(Qt.CheckState.Checked) self.items.append(i) l.addWidget(self.books) self.bb = bb = QDialogButtonBox( QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) self.a = b = bb.addButton(_('Select &all'), QDialogButtonBox.ButtonRole.ActionRole) b.clicked.connect(self.select_all), b.setIcon(QIcon(I('plus.png'))) self.n = b = bb.addButton(_('Select &none'), QDialogButtonBox.ButtonRole.ActionRole) b.clicked.connect(self.select_none), b.setIcon(QIcon(I('minus.png'))) self.ctc = b = bb.addButton(_('&Copy to clipboard'), QDialogButtonBox.ButtonRole.ActionRole) b.clicked.connect(self.copy_to_clipboard), b.setIcon( QIcon(I('edit-copy.png'))) l.addWidget(bb) self.resize(600, 400)