def setup_ui(self): from calibre.gui2.convert.look_and_feel_ui import Ui_Form f, w = Ui_Form(), QWidget() f.setupUi(w) self.l = l = QFormLayout(self) self.setLayout(l) l.addRow(QLabel(_('Select what style information you want completely removed:'))) self.h = h = QHBoxLayout() for name, text in ( ('fonts', _('&Fonts')), ('margins', _('&Margins')), ('padding', _('&Padding')), ('floats', _('Flo&ats')), ('colors', _('&Colors')), ): c = QCheckBox(text) setattr(self, 'opt_' + name, c) h.addWidget(c) c.setToolTip(getattr(f, 'filter_css_' + name).toolTip()) l.addRow(h) self.others = o = QLineEdit(self) l.addRow(_('&Other CSS properties:'), o) o.setToolTip(f.filter_css_others.toolTip()) if self.current_name is not None: self.filter_current = c = QCheckBox(_('Only filter CSS in the current file (%s)') % self.current_name) l.addRow(c) l.addRow(self.bb)
def setup_ui(self): from calibre.gui2.convert.look_and_feel_ui import Ui_Form f, w = Ui_Form(), QWidget() f.setupUi(w) self.l = l = QFormLayout(self) self.setLayout(l) l.addRow(QLabel(_("Select what style information you want completely removed:"))) self.h = h = QHBoxLayout() for name, text in { "fonts": _("&Fonts"), "margins": _("&Margins"), "padding": _("&Padding"), "floats": _("Flo&ats"), "colors": _("&Colors"), }.iteritems(): c = QCheckBox(text) setattr(self, "opt_" + name, c) h.addWidget(c) c.setToolTip(getattr(f, "filter_css_" + name).toolTip()) l.addRow(h) self.others = o = QLineEdit(self) l.addRow(_("&Other CSS properties:"), o) o.setToolTip(f.filter_css_others.toolTip()) if self.current_name is not None: self.filter_current = c = QCheckBox(_("Only filter CSS in the current file (%s)") % self.current_name) l.addRow(c) l.addRow(self.bb)
def setup_ui(self, parent): self.make_widgets(parent, EditWithComplete) values = self.all_values = list(self.db.all_custom(num=self.col_id)) values.sort(key=sort_key) self.main_widget.setSizeAdjustPolicy(self.main_widget.AdjustToMinimumContentsLengthWithIcon) self.main_widget.setMinimumContentsLength(25) self.widgets.append(QLabel('', parent)) w = QWidget(parent) layout = QHBoxLayout(w) layout.setContentsMargins(0, 0, 0, 0) self.remove_series = QCheckBox(parent) self.remove_series.setText(_('Remove series')) layout.addWidget(self.remove_series) self.idx_widget = QCheckBox(parent) self.idx_widget.setText(_('Automatically number books')) layout.addWidget(self.idx_widget) self.force_number = QCheckBox(parent) self.force_number.setText(_('Force numbers to start with ')) layout.addWidget(self.force_number) self.series_start_number = QSpinBox(parent) self.series_start_number.setMinimum(1) self.series_start_number.setMaximum(9999999) self.series_start_number.setProperty("value", 1) layout.addWidget(self.series_start_number) layout.addItem(QSpacerItem(20, 10, QSizePolicy.Expanding, QSizePolicy.Minimum)) self.widgets.append(w) self.idx_widget.stateChanged.connect(self.check_changed_checkbox) self.force_number.stateChanged.connect(self.check_changed_checkbox) self.series_start_number.valueChanged.connect(self.check_changed_checkbox) self.remove_series.stateChanged.connect(self.check_changed_checkbox) self.ignore_change_signals = False
def __init__(self): QWidget.__init__(self) self.layout = QGridLayout() self.setLayout(self.layout) labelColumnWidths = [] self.opdsUrlLabel = QLabel('OPDS URL: ') self.layout.addWidget(self.opdsUrlLabel, 0, 0) labelColumnWidths.append(self.layout.itemAtPosition(0, 0).sizeHint().width()) print type(prefs.defaults['opds_url']) print type(prefs['opds_url']) convertSingleStringOpdsUrlPreferenceToListOfStringsPreference() self.opdsUrlEditor = QComboBox(self) self.opdsUrlEditor.addItems(prefs['opds_url']) self.opdsUrlEditor.setEditable(True) self.opdsUrlEditor.setInsertPolicy(QComboBox.InsertAtTop) self.layout.addWidget(self.opdsUrlEditor, 0, 1) self.opdsUrlLabel.setBuddy(self.opdsUrlEditor) self.hideNewsCheckbox = QCheckBox('Hide Newspapers', self) self.hideNewsCheckbox.setChecked(prefs['hideNewspapers']) self.layout.addWidget(self.hideNewsCheckbox, 1, 0) labelColumnWidths.append(self.layout.itemAtPosition(1, 0).sizeHint().width()) self.hideBooksAlreadyInLibraryCheckbox = QCheckBox('Hide books already in library', self) self.hideBooksAlreadyInLibraryCheckbox.setChecked(prefs['hideBooksAlreadyInLibrary']) self.layout.addWidget(self.hideBooksAlreadyInLibraryCheckbox, 2, 0) labelColumnWidths.append(self.layout.itemAtPosition(2, 0).sizeHint().width()) labelColumnWidth = max(labelColumnWidths) self.layout.setColumnMinimumWidth(1, labelColumnWidth * 2)
def setup_store_checks(self): first_run = self.config.get('first_run', True) # Add check boxes for each store so the user # can disable searching specific stores on a # per search basis. existing = {} for n in self.store_checks: existing[n] = self.store_checks[n].isChecked() self.store_checks = {} stores_check_widget = QWidget() store_list_layout = QGridLayout() stores_check_widget.setLayout(store_list_layout) icon = QIcon(I('donate.png')) for i, x in enumerate(sorted(self.gui.istores.keys(), key=lambda x: x.lower())): cbox = QCheckBox(x) cbox.setChecked(existing.get(x, first_run)) store_list_layout.addWidget(cbox, i, 0, 1, 1) if self.gui.istores[x].base_plugin.affiliate: iw = QLabel(self) iw.setToolTip('<p>' + _('Buying from this store supports the calibre developer: %s</p>') % self.gui.istores[x].base_plugin.author + '</p>') iw.setPixmap(icon.pixmap(16, 16)) store_list_layout.addWidget(iw, i, 1, 1, 1) self.store_checks[x] = cbox store_list_layout.setRowStretch(store_list_layout.rowCount(), 10) self.store_list.setWidget(stores_check_widget) self.config['first_run'] = False
def __init__(self): QWidget.__init__(self) self.l = QVBoxLayout() self.setLayout(self.l) self.spoilers = QCheckBox('Use spoilers when creating x-ray') self.spoilers.setChecked(prefs['spoilers']) self.l.addWidget(self.spoilers) self.send_to_device = QCheckBox('Send x-ray to device if connected') self.send_to_device.setChecked(prefs['send_to_device']) self.l.addWidget(self.send_to_device) self.create_xray_when_sending = QCheckBox('Create x-ray for files that don\'t already have them when sending to device') self.create_xray_when_sending.setChecked(prefs['create_xray_when_sending']) self.l.addWidget(self.create_xray_when_sending) self.book_types_to_create = QGroupBox() self.book_types_to_create.setTitle('Book types to create x-ray files for') self.book_types_to_create.setLayout(QHBoxLayout (self.book_types_to_create)) self.mobi = QCheckBox('MOBI') self.mobi.setChecked(prefs['mobi']) self.book_types_to_create.layout().addWidget(self.mobi) self.azw3 = QCheckBox('AZW3') self.azw3.setChecked(prefs['azw3']) self.book_types_to_create.layout().addWidget(self.azw3) self.l.addWidget(self.book_types_to_create)
def __init__(self): QWidget.__init__(self) self.l = QVBoxLayout() self.setLayout(self.l) self.cbox1 = QCheckBox("Reformatting") self.cbox1.setChecked(prefs["reformat"]) self.l.addWidget(self.cbox1) self.cbox5 = QCheckBox("Paragraph by Ending mark") self.cbox5.setChecked(prefs["para_by_mark"]) self.l.addWidget(self.cbox5) self.cbox2 = QCheckBox("Pretty Quote char") self.cbox2.setChecked(prefs["pretty_quote"]) self.l.addWidget(self.cbox2) self.cbox3 = QCheckBox("Guess Chapter") self.cbox3.setChecked(prefs["guess_chapter"]) self.l.addWidget(self.cbox3) self.cbox4 = QCheckBox("Allow Empty Paragraph") self.cbox4.setChecked(prefs["insert_empty_paragraph"]) self.l.addWidget(self.cbox4) cl = QHBoxLayout() cl.addWidget(QLabel("Broken Word over lines")) self.combo1 = QComboBox() wbrk_list = ["None", "Pattern", "Naver"] self.combo1.addItems(wbrk_list) self.combo1.setCurrentIndex(wbrk_list.index(prefs["correct_word_break"])) cl.addWidget(self.combo1) self.l.addLayout(cl)
class UpdateNotification(QDialog): def __init__(self, calibre_version, plugin_updates, parent=None): QDialog.__init__(self, parent) self.setAttribute(Qt.WA_QuitOnClose, False) self.resize(400, 250) self.l = QGridLayout() self.setLayout(self.l) self.logo = QLabel() self.logo.setMaximumWidth(110) self.logo.setPixmap(QPixmap(I('lt.png')).scaled(100, 100, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)) ver = calibre_version if ver.endswith('.0'): ver = ver[:-2] self.label = QLabel(('<p>'+ _('New version <b>%(ver)s</b> of %(app)s is available for download. ' 'See the <a href="http://calibre-ebook.com/whats-new' '">new features</a>.'))%dict( app=__appname__, ver=ver)) self.label.setOpenExternalLinks(True) self.label.setWordWrap(True) self.setWindowTitle(_('Update available!')) self.setWindowIcon(QIcon(I('lt.png'))) self.l.addWidget(self.logo, 0, 0) self.l.addWidget(self.label, 0, 1) self.cb = QCheckBox( _('Show this notification for future updates'), self) self.l.addWidget(self.cb, 1, 0, 1, -1) self.cb.setChecked(config.get('new_version_notification')) self.cb.stateChanged.connect(self.show_future) self.bb = QDialogButtonBox(self) b = self.bb.addButton(_('&Get update'), self.bb.AcceptRole) b.setDefault(True) b.setIcon(QIcon(I('arrow-down.png'))) if plugin_updates > 0: b = self.bb.addButton(_('Update &plugins'), self.bb.ActionRole) b.setIcon(QIcon(I('plugins/plugin_updater.png'))) b.clicked.connect(self.get_plugins, type=Qt.QueuedConnection) self.bb.addButton(self.bb.Cancel) self.l.addWidget(self.bb, 2, 0, 1, -1) self.bb.accepted.connect(self.accept) self.bb.rejected.connect(self.reject) dynamic.set('update to version %s'%calibre_version, False) def get_plugins(self): from calibre.gui2.dialogs.plugin_updater import (PluginUpdaterDialog, FILTER_UPDATE_AVAILABLE) d = PluginUpdaterDialog(self.parent(), initial_filter=FILTER_UPDATE_AVAILABLE) d.exec_() def show_future(self, *args): config.set('new_version_notification', bool(self.cb.isChecked())) def accept(self): open_url(QUrl(get_download_url())) QDialog.accept(self)
def cb(name, text, tt=''): ans = QCheckBox(text) l.addWidget(ans) prefs_key = ans.prefs_key = 'choose-merge-cb-' + name ans.setChecked(gprefs.get(prefs_key, True)) connect_lambda(ans.stateChanged, self, lambda self, state: self.state_changed(getattr(self, name), state), type=Qt.QueuedConnection) if tt: ans.setToolTip(tt) setattr(self, name, ans) return ans
def setup_ui(self, parent): self.make_widgets(parent, EditWithComplete) values = self.all_values = list(self.db.all_custom(num=self.col_id)) values.sort(key=sort_key) self.main_widget.setSizeAdjustPolicy(self.main_widget.AdjustToMinimumContentsLengthWithIcon) self.main_widget.setMinimumContentsLength(25) self.widgets.append(QLabel('', parent)) w = QWidget(parent) layout = QHBoxLayout(w) layout.setContentsMargins(0, 0, 0, 0) self.remove_series = QCheckBox(parent) self.remove_series.setText(_('Clear series')) layout.addWidget(self.remove_series) self.idx_widget = QCheckBox(parent) self.idx_widget.setText(_('Automatically number books')) self.idx_widget.setToolTip('<p>' + _( 'If not checked, the series number for the books will be set to 1. ' 'If checked, selected books will be automatically numbered, ' 'in the order you selected them. So if you selected ' 'Book A and then Book B, Book A will have series number 1 ' 'and Book B series number 2.') + '</p>') layout.addWidget(self.idx_widget) self.force_number = QCheckBox(parent) self.force_number.setText(_('Force numbers to start with ')) self.force_number.setToolTip('<p>' + _( 'Series will normally be renumbered from the highest ' 'number in the database for that series. Checking this ' 'box will tell calibre to start numbering from the value ' 'in the box') + '</p>') layout.addWidget(self.force_number) self.series_start_number = QDoubleSpinBox(parent) self.series_start_number.setMinimum(0.0) self.series_start_number.setMaximum(9999999.0) self.series_start_number.setProperty("value", 1.0) layout.addWidget(self.series_start_number) self.series_increment = QDoubleSpinBox(parent) self.series_increment.setMinimum(0.00) self.series_increment.setMaximum(99999.0) self.series_increment.setProperty("value", 1.0) self.series_increment.setToolTip('<p>' + _( 'The amount by which to increment the series number ' 'for successive books. Only applicable when using ' 'force series numbers.') + '</p>') self.series_increment.setPrefix('+') layout.addWidget(self.series_increment) layout.addItem(QSpacerItem(20, 10, QSizePolicy.Expanding, QSizePolicy.Minimum)) self.widgets.append(w) self.idx_widget.stateChanged.connect(self.a_c_checkbox_changed) self.force_number.stateChanged.connect(self.a_c_checkbox_changed) self.series_start_number.valueChanged.connect(self.a_c_checkbox_changed) self.series_increment.valueChanged.connect(self.a_c_checkbox_changed) self.remove_series.stateChanged.connect(self.a_c_checkbox_changed) self.main_widget self.ignore_change_signals = False
def __init__(self, plugin_action): QWidget.__init__(self) self.plugin_action = plugin_action layout = QVBoxLayout(self) self.setLayout(layout) # --- Directory Options --- directory_group_box = QGroupBox(_('Default Unpack Directory:'), self) layout.addWidget(directory_group_box) directory_group_box_layout = QVBoxLayout() directory_group_box.setLayout(directory_group_box_layout) # Directory path Textbox # Load the textbox with the current preference setting self.directory_txtBox = QLineEdit(plugin_prefs['Unpack_Folder'], self) self.directory_txtBox.setToolTip(_('<p>Default directory to extract files to')) directory_group_box_layout.addWidget(self.directory_txtBox) self.directory_txtBox.setReadOnly(True) # Folder select button directory_button = QPushButton(_('Select/Change Unpack Directory'), self) directory_button.setToolTip(_('<p>Select/Change directory to extract files to.')) # Connect button to the getDirectory function directory_button.clicked.connect(self.getDirectory) directory_group_box_layout.addWidget(directory_button) self.default_folder_check = QCheckBox(_('Always use the Default Unpack Directory'), self) self.default_folder_check.setToolTip(_('<p>When unchecked... you will be prompted to select a destination '+ 'directory for the extracted content each time you use Mobiunpack.')) directory_group_box_layout.addWidget(self.default_folder_check) # Load the checkbox with the current preference setting self.default_folder_check.setChecked(plugin_prefs['Always_Use_Unpack_Folder']) misc_group_box = QGroupBox(_('Default settings:'), self) layout.addWidget(misc_group_box) misc_group_box_layout = QVBoxLayout() misc_group_box.setLayout(misc_group_box_layout) self.use_hd_images = QCheckBox(_('Always use HD images if present'), self) self.use_hd_images.setToolTip(_('<p>When checked... any HD images present in the kindlebook '+ 'will be used for creating the ePub.')) misc_group_box_layout.addWidget(self.use_hd_images) # Load the checkbox with the current preference setting self.use_hd_images.setChecked(plugin_prefs['Use_HD_Images']) combo_label = QLabel('Select epub version output:', self) misc_group_box_layout.addWidget(combo_label) self.epub_version_combobox = QComboBox() self.epub_version_combobox.setToolTip(_('<p>Select the type of OPF file to create.')) misc_group_box_layout.addWidget(self.epub_version_combobox) self.epub_version_combobox.addItems(['Auto-detect', 'ePub2', 'ePub3']) if plugin_prefs['Epub_Version'] == 'A': self.epub_version_combobox.setCurrentIndex(0) else: self.epub_version_combobox.setCurrentIndex(int(plugin_prefs['Epub_Version'])-1)
def __init__(self, prefs, configName, title): QWidget.__init__(self) self.l = QVBoxLayout() self.setLayout(self.l) self.configName = configName self.hotkeyLayout = QHBoxLayout() self.l.addLayout(self.hotkeyLayout) enabledLabel = QLabel(title) self.hotkeyLayout.addWidget(enabledLabel) self.enabledBox = QCheckBox() self.hotkeyLayout.addWidget(self.enabledBox) self.enabledBox.setChecked(prefs[configName + '_hotkey_enabled']) hotkeyLayout2 = QHBoxLayout() self.l.addLayout(hotkeyLayout2) ctrlLabel = QLabel("Ctrl") self.ctrlBox = QCheckBox() self.ctrlBox.setChecked(prefs[configName + '_hotkey_ctrl']) ctrlLabel.setBuddy(self.ctrlBox) hotkeyLayout2.addWidget(ctrlLabel) hotkeyLayout2.addWidget(self.ctrlBox) altLabel = QLabel("Alt") self.altBox = QCheckBox() self.altBox.setChecked(prefs[configName + '_hotkey_alt']) altLabel.setBuddy(self.altBox) hotkeyLayout2.addWidget(altLabel) hotkeyLayout2.addWidget(self.altBox) shiftLabel = QLabel("Shift") self.shiftBox = QCheckBox() self.shiftBox.setChecked(prefs[configName + '_hotkey_shift']) shiftLabel.setBuddy(self.shiftBox) hotkeyLayout2.addWidget(shiftLabel) hotkeyLayout2.addWidget(self.shiftBox) self.keycodeBox = QComboBox() for key, value in keycodes.iteritems(): self.keycodeBox.addItem(key, value) index = self.keycodeBox.findData(prefs[configName + '_hotkey_keycode']) if index != -1: self.keycodeBox.setCurrentIndex(index) hotkeyLayout2.addWidget(self.keycodeBox)
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) self.applyall = QCheckBox(_('Apply to all EPUBs?'),self) self.applyall.setToolTip(_('Apply the same action to the rest of the EPUBs after this.')) layout.addWidget(self.applyall) 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): QWidget.__init__(self, parent) self.setVisible(False) parent.installEventFilter(self) self._show_fraction = 0.0 self.show_animation = a = QPropertyAnimation(self, "show_fraction", self) a.setDuration(1000), a.setEasingCurve(QEasingCurve.OutQuad) a.setStartValue(0.0), a.setEndValue(1.0) a.finished.connect(self.stop_show_animation) self.rendered_pixmap = None self.questions = [] self.icon = ic = Icon(self) self.msg_label = msg = QLabel("some random filler text") msg.setWordWrap(True) self.bb = QDialogButtonBox() self.bb.accepted.connect(self.accept) self.bb.rejected.connect(self.reject) self.log_button = self.bb.addButton(_("View log"), self.bb.ActionRole) self.log_button.setIcon(QIcon(I("debug.png"))) self.log_button.clicked.connect(self.show_log) self.copy_button = self.bb.addButton(_("&Copy to clipboard"), self.bb.ActionRole) self.copy_button.clicked.connect(self.copy_to_clipboard) self.action_button = self.bb.addButton("", self.bb.ActionRole) self.action_button.clicked.connect(self.action_clicked) self.show_det_msg = _("Show &details") self.hide_det_msg = _("Hide &details") self.det_msg_toggle = self.bb.addButton(self.show_det_msg, self.bb.ActionRole) self.det_msg_toggle.clicked.connect(self.toggle_det_msg) self.det_msg_toggle.setToolTip(_("Show detailed information about this error")) self.det_msg = PlainTextEdit(self) self.det_msg.setReadOnly(True) self.bb.setStandardButtons(self.bb.Yes | self.bb.No | self.bb.Ok) self.bb.button(self.bb.Yes).setDefault(True) self.title_label = title = QLabel("A dummy title") f = title.font() f.setBold(True) title.setFont(f) self.checkbox = QCheckBox("", self) self._l = l = QVBoxLayout(self) self._h = h = QHBoxLayout() self._v = v = QVBoxLayout() v.addWidget(title), v.addWidget(msg) h.addWidget(ic), h.addSpacing(10), h.addLayout(v), l.addLayout(h) l.addSpacing(5) l.addWidget(self.checkbox) l.addWidget(self.det_msg) l.addWidget(self.bb) self.ask_question.connect(self.do_ask_question, type=Qt.QueuedConnection) self.setFocusPolicy(Qt.NoFocus) for child in self.findChildren(QWidget): child.setFocusPolicy(Qt.NoFocus) self.setFocusProxy(self.parent()) self.resize_timer = t = QTimer(self) t.setSingleShot(True), t.setInterval(100), t.timeout.connect(self.parent_resized)
def __init__(self, parent): QDialog.__init__(self, parent) self.gui = parent self.setAttribute(Qt.WA_DeleteOnClose, False) self.setWindowIcon(QIcon(I('polish.png'))) self.reports = [] self.l = l = QGridLayout() self.setLayout(l) self.view = v = QTextEdit(self) v.setReadOnly(True) l.addWidget(self.view, 0, 0, 1, 2) self.backup_msg = la = QLabel('') l.addWidget(la, 1, 0, 1, 2) la.setVisible(False) la.setWordWrap(True) self.ign = QCheckBox(_('Ignore remaining report'), self) l.addWidget(self.ign, 2, 0) bb = self.bb = QDialogButtonBox(QDialogButtonBox.Close) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) b = self.log_button = bb.addButton(_('View full &log'), bb.ActionRole) b.clicked.connect(self.view_log) bb.button(bb.Close).setDefault(True) l.addWidget(bb, 2, 1) self.finished.connect(self.show_next, type=Qt.QueuedConnection) self.resize(QSize(800, 600))
def __init__(self, parent, prefs): QDialog.__init__(self, parent) self.prefs = prefs self.setWindowTitle(_('Create ToC from XPath')) self.l = l = QVBoxLayout() self.setLayout(l) self.la = la = QLabel(_( 'Specify a series of XPath expressions for the different levels of' ' the Table of Contents. You can use the wizard buttons to help' ' you create XPath expressions.')) la.setWordWrap(True) l.addWidget(la) self.widgets = [] for i in range(5): la = _('Level %s ToC:')%('&%d'%(i+1)) xp = XPathEdit(self) xp.set_msg(la) self.widgets.append(xp) l.addWidget(xp) self.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) self.ssb = b = bb.addButton(_('&Save settings'), bb.ActionRole) b.clicked.connect(self.save_settings) self.load_button = b = bb.addButton(_('&Load settings'), bb.ActionRole) self.load_menu = QMenu(b) b.setMenu(self.load_menu) self.setup_load_button() self.remove_duplicates_cb = QCheckBox(_('Do not add duplicate entries at the same level')) self.remove_duplicates_cb.setChecked(self.prefs.get('xpath_toc_remove_duplicates', True)) l.addWidget(self.remove_duplicates_cb) l.addStretch() l.addWidget(bb) self.resize(self.sizeHint() + QSize(50, 75))
def __init__(self, fmts, parent=None): QDialog.__init__(self, parent) self.l = l = QVBoxLayout(self) self.setLayout(l) self.setWindowTitle(_('Choose format to edit')) self.la = la = QLabel(_( 'This book has multiple formats that can be edited. Choose the format you want to edit.')) l.addWidget(la) self.rem = QCheckBox(_('Always ask when more than one format is available')) self.rem.setChecked(True) l.addWidget(self.rem) self.bb = bb = QDialogButtonBox(self) l.addWidget(bb) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) self.buts = buts = [] for fmt in fmts: b = bb.addButton(fmt.upper(), bb.AcceptRole) b.clicked.connect(partial(self.chosen, fmt)) buts.append(b) self.fmt = None self.resize(self.sizeHint())
def make_widgets(self, parent, main_widget_class, extra_label_text=''): w = QWidget(parent) self.widgets = [QLabel('&'+self.col_metadata['name']+':', w), w] l = QHBoxLayout() l.setContentsMargins(0, 0, 0, 0) w.setLayout(l) self.main_widget = main_widget_class(w) l.addWidget(self.main_widget) l.setStretchFactor(self.main_widget, 10) self.a_c_checkbox = QCheckBox(_('Apply changes'), w) l.addWidget(self.a_c_checkbox) self.ignore_change_signals = True # connect to the various changed signals so we can auto-update the # apply changes checkbox if hasattr(self.main_widget, 'editTextChanged'): # editable combobox widgets self.main_widget.editTextChanged.connect(self.a_c_checkbox_changed) if hasattr(self.main_widget, 'textChanged'): # lineEdit widgets self.main_widget.textChanged.connect(self.a_c_checkbox_changed) if hasattr(self.main_widget, 'currentIndexChanged'): # combobox widgets self.main_widget.currentIndexChanged[int].connect(self.a_c_checkbox_changed) if hasattr(self.main_widget, 'valueChanged'): # spinbox widgets self.main_widget.valueChanged.connect(self.a_c_checkbox_changed) if hasattr(self.main_widget, 'dateTimeChanged'): # dateEdit widgets self.main_widget.dateTimeChanged.connect(self.a_c_checkbox_changed)
class Ui_Dialog(object): def setupUi(self, Dialog): Dialog.setObjectName(_fromUtf8("Dialog")) Dialog.resize(497, 235) self.gridLayout = QGridLayout(Dialog) self.gridLayout.setObjectName(_fromUtf8("gridLayout")) self.icon_label = QLabel(Dialog) self.icon_label.setMaximumSize(QtCore.QSize(COVER_ICON_SIZE, COVER_ICON_SIZE)) self.icon_label.setText(_fromUtf8("")) self.icon_label.setPixmap(QPixmap(_fromUtf8(I("dialog_warning.png")))) self.icon_label.setScaledContents(False) self.icon_label.setObjectName(_fromUtf8("icon_label")) self.gridLayout.addWidget(self.icon_label, 0, 0, 1, 1) self.msg = QLabel(Dialog) self.msg.setText(_fromUtf8("")) self.msg.setWordWrap(True) self.msg.setOpenExternalLinks(True) self.msg.setObjectName(_fromUtf8("msg")) self.gridLayout.addWidget(self.msg, 0, 1, 1, 1) self.det_msg = QPlainTextEdit(Dialog) self.det_msg.setReadOnly(True) self.det_msg.setObjectName(_fromUtf8("det_msg")) self.gridLayout.addWidget(self.det_msg, 1, 0, 1, 2) self.bb = QDialogButtonBox(Dialog) self.bb.setOrientation(QtCore.Qt.Horizontal) self.bb.setStandardButtons(QDialogButtonBox.Ok) self.bb.setObjectName(_fromUtf8("bb")) self.gridLayout.addWidget(self.bb, 3, 0, 1, 2) self.toggle_checkbox = QCheckBox(Dialog) self.toggle_checkbox.setText(_fromUtf8("")) self.toggle_checkbox.setObjectName(_fromUtf8("toggle_checkbox")) self.gridLayout.addWidget(self.toggle_checkbox, 2, 0, 1, 2) self.retranslateUi(Dialog) self.bb.accepted.connect(Dialog.accept) self.bb.rejected.connect(Dialog.reject) QtCore.QMetaObject.connectSlotsByName(Dialog) def retranslateUi(self, Dialog): Dialog.setWindowTitle(_("Dialog"))
def __init__(self, parent): QDialog.__init__(self, parent) self.setAttribute(Qt.WA_DeleteOnClose, False) self.setWindowIcon(QIcon(I('dialog_question.png'))) self.questions = [] self._l = l = QGridLayout(self) self.setLayout(l) self.icon_label = ic = QLabel(self) ic.setPixmap(QPixmap(I('dialog_question.png'))) self.msg_label = msg = QLabel('some random filler text') msg.setWordWrap(True) ic.setMaximumWidth(110) ic.setMaximumHeight(100) ic.setScaledContents(True) ic.setStyleSheet('QLabel { margin-right: 10px }') self.bb = QDialogButtonBox() self.bb.accepted.connect(self.accept) self.bb.rejected.connect(self.reject) self.log_button = self.bb.addButton(_('View log'), self.bb.ActionRole) self.log_button.setIcon(QIcon(I('debug.png'))) self.log_button.clicked.connect(self.show_log) self.copy_button = self.bb.addButton(_('&Copy to clipboard'), self.bb.ActionRole) self.copy_button.clicked.connect(self.copy_to_clipboard) self.action_button = self.bb.addButton('', self.bb.ActionRole) self.action_button.clicked.connect(self.action_clicked) self.show_det_msg = _('Show &details') self.hide_det_msg = _('Hide &details') self.det_msg_toggle = self.bb.addButton(self.show_det_msg, self.bb.ActionRole) self.det_msg_toggle.clicked.connect(self.toggle_det_msg) self.det_msg_toggle.setToolTip( _('Show detailed information about this error')) self.det_msg = QPlainTextEdit(self) self.det_msg.setReadOnly(True) self.bb.setStandardButtons(self.bb.Yes|self.bb.No|self.bb.Ok) self.bb.button(self.bb.Yes).setDefault(True) self.checkbox = QCheckBox('', self) l.addWidget(ic, 0, 0, 1, 1) l.addWidget(msg, 0, 1, 1, 1) l.addWidget(self.checkbox, 1, 0, 1, 2) l.addWidget(self.det_msg, 2, 0, 1, 2) l.addWidget(self.bb, 3, 0, 1, 2) self.ask_question.connect(self.do_ask_question, type=Qt.QueuedConnection)
def add_path_row(self, path, watched): # path path_line = QLineEdit(path) path_line.setReadOnly(True) width = QFontMetrics(QFont()).width(path) * 1.05 path_line.setMinimumWidth(width) self._paths_layout.addWidget(path_line, self._paths_row, 0) # watch label and checkbox watcher_label = QLabel('watch:') self._paths_layout.addWidget(watcher_label, self._paths_row, 1) watcher_check_box = QCheckBox() watcher_check_box.setChecked(watched) self._watch_check_boxes[path] = watcher_check_box self._paths_layout.addWidget(watcher_check_box, self._paths_row, 2) # remove button remove_button = QRemovePushButton(path, path_line, watcher_label, watcher_check_box, self._removes, self._adds) remove_button.clicked.connect(remove_button.remove) self._paths_layout.addWidget(remove_button, self._paths_row, 3) self._paths_row += 1
class Choose(QDialog): def __init__(self, fmts, parent=None): QDialog.__init__(self, parent) self.l = l = QVBoxLayout(self) self.setLayout(l) self.setWindowTitle(_('Choose format to edit')) self.la = la = QLabel(_( 'This book has multiple formats that can be edited. Choose the format you want to edit.')) l.addWidget(la) self.rem = QCheckBox(_('Always ask when more than one format is available')) self.rem.setChecked(True) l.addWidget(self.rem) self.bb = bb = QDialogButtonBox(self) l.addWidget(bb) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) self.buts = buts = [] for fmt in fmts: b = bb.addButton(fmt.upper(), bb.AcceptRole) b.setObjectName(fmt) connect_lambda(b.clicked, self, lambda self: self.chosen(self.sender().objectName())) buts.append(b) self.fmt = None self.resize(self.sizeHint()) def chosen(self, fmt): self.fmt = fmt def accept(self): from calibre.gui2.tweak_book import tprefs tprefs['choose_tweak_fmt'] = self.rem.isChecked() QDialog.accept(self)
def __init__(self, formats, parent=None): QDialog.__init__(self, parent) self.setWindowTitle(_('Choose format to edit')) self.setWindowIcon(QIcon(I('dialog_question.png'))) l = self.l = QGridLayout() self.setLayout(l) la = self.la = QLabel(_('Choose which format you want to edit:')) formats = sorted(formats) l.addWidget(la, 0, 0, 1, -1) self.buttons = [] for i, f in enumerate(formats): b = QCheckBox('&' + f, self) l.addWidget(b, 1, i) self.buttons.append(b) if i == 0: b.setChecked(True) bb = self.bb = QDialogButtonBox( QDialogButtonBox.Ok|QDialogButtonBox.Cancel) bb.addButton(_('&All formats'), bb.ActionRole).clicked.connect(self.do_all) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) l.addWidget(bb, l.rowCount(), 0, 1, -1) self.resize(self.sizeHint())
def __init__(self, parent): QDialog.__init__(self, parent) self.setAttribute(Qt.WA_DeleteOnClose, False) self.queue = [] self.do_pop.connect(self.pop, type=Qt.QueuedConnection) self._layout = l = QGridLayout() self.setLayout(l) self.icon = QIcon(I('dialog_error.png')) self.setWindowIcon(self.icon) self.icon_label = QLabel() self.icon_label.setPixmap(self.icon.pixmap(68, 68)) self.icon_label.setMaximumSize(QSize(68, 68)) self.msg_label = QLabel('<p> ') self.msg_label.setStyleSheet('QLabel { margin-top: 1ex; }') self.msg_label.setWordWrap(True) self.msg_label.setTextFormat(Qt.RichText) self.det_msg = QPlainTextEdit(self) self.det_msg.setVisible(False) self.bb = QDialogButtonBox(QDialogButtonBox.Close, parent=self) self.bb.accepted.connect(self.accept) self.bb.rejected.connect(self.reject) self.ctc_button = self.bb.addButton(_('&Copy to clipboard'), self.bb.ActionRole) self.ctc_button.clicked.connect(self.copy_to_clipboard) self.retry_button = self.bb.addButton(_('&Retry'), self.bb.ActionRole) self.retry_button.clicked.connect(self.retry) self.retry_func = None self.show_det_msg = _('Show &details') self.hide_det_msg = _('Hide &details') self.det_msg_toggle = self.bb.addButton(self.show_det_msg, self.bb.ActionRole) self.det_msg_toggle.clicked.connect(self.toggle_det_msg) self.det_msg_toggle.setToolTip( _('Show detailed information about this error')) self.suppress = QCheckBox(self) l.addWidget(self.icon_label, 0, 0, 1, 1) l.addWidget(self.msg_label, 0, 1, 1, 1) l.addWidget(self.det_msg, 1, 0, 1, 2) l.addWidget(self.suppress, 2, 0, 1, 2, Qt.AlignLeft|Qt.AlignBottom) l.addWidget(self.bb, 3, 0, 1, 2, Qt.AlignRight|Qt.AlignBottom) l.setColumnStretch(1, 100) self.setModal(False) self.suppress.setVisible(False) self.do_resize()
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, QCheckBox) config_dialog = QDialog(parent) button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) v = QVBoxLayout(config_dialog) def size_dialog(): config_dialog.resize(config_dialog.sizeHint()) button_box.accepted.connect(config_dialog.accept) button_box.rejected.connect(config_dialog.reject) config_dialog.setWindowTitle(_('Customize') + ' ' + self.name) from calibre.customize.ui import (plugin_customization, customize_plugin) help_text = self.customization_help(gui=True) help_text = QLabel(help_text, config_dialog) help_text.setWordWrap(True) help_text.setTextInteractionFlags(Qt.LinksAccessibleByMouse | Qt.LinksAccessibleByKeyboard) help_text.setOpenExternalLinks(True) v.addWidget(help_text) bf = QCheckBox(_('Add linked files in breadth first order')) bf.setToolTip(_('Normally, when following links in HTML files' ' calibre does it depth first, i.e. if file A links to B and ' ' C, but B links to D, the files are added in the order A, B, D, C. ' ' With this option, they will instead be added as A, B, C, D')) sc = plugin_customization(self) if not sc: sc = '' sc = sc.strip() enc = sc.partition('|')[0] bfs = sc.partition('|')[-1] bf.setChecked(bfs == 'bf') sc = QLineEdit(enc, config_dialog) v.addWidget(sc) v.addWidget(bf) v.addWidget(button_box) size_dialog() config_dialog.exec_() if config_dialog.result() == QDialog.Accepted: sc = unicode(sc.text()).strip() if bf.isChecked(): sc += '|bf' customize_plugin(self, sc) return config_dialog.result()
def __init__(self, calibre_version, plugin_updates, parent=None): QDialog.__init__(self, parent) self.setAttribute(Qt.WA_QuitOnClose, False) self.resize(400, 250) self.l = QGridLayout() self.setLayout(self.l) self.logo = QLabel() self.logo.setMaximumWidth(110) self.logo.setPixmap(QIcon(I('lt.png')).pixmap(100, 100)) ver = calibre_version if ver.endswith('.0'): ver = ver[:-2] self.label = QLabel(('<p>'+ _( 'New version <b>{ver}</b> of {app} is available for download. ' 'See the <a href="{url}">new features</a>.').format( url=localize_website_link('https://calibre-ebook.com/whats-new'), app=__appname__, ver=ver))) self.label.setOpenExternalLinks(True) self.label.setWordWrap(True) self.setWindowTitle(_('Update available!')) self.setWindowIcon(QIcon(I('lt.png'))) self.l.addWidget(self.logo, 0, 0) self.l.addWidget(self.label, 0, 1) self.cb = QCheckBox( _('Show this notification for future updates'), self) self.l.addWidget(self.cb, 1, 0, 1, -1) self.cb.setChecked(config.get('new_version_notification')) self.cb.stateChanged.connect(self.show_future) self.bb = QDialogButtonBox(self) b = self.bb.addButton(_('&Get update'), self.bb.AcceptRole) b.setDefault(True) b.setIcon(QIcon(I('arrow-down.png'))) if plugin_updates > 0: b = self.bb.addButton(_('Update &plugins'), self.bb.ActionRole) b.setIcon(QIcon(I('plugins/plugin_updater.png'))) b.clicked.connect(self.get_plugins, type=Qt.QueuedConnection) self.bb.addButton(self.bb.Cancel) self.l.addWidget(self.bb, 2, 0, 1, -1) self.bb.accepted.connect(self.accept) self.bb.rejected.connect(self.reject) dynamic.set(version_key(calibre_version), False)
def __init__(self): QWidget.__init__(self) self.l = QVBoxLayout() self.setLayout(self.l) #add checkbox self.debug_checkbox = QCheckBox('Debug Mode') self.debug_checkbox.setChecked(prefs['debug_mode']) self.l.addWidget(self.debug_checkbox) self.config_url = QUrl('https://'+prefs['api_base']+'/link/start') self.webview = QTWebView(bearer_token=prefs['token']) self.webview.load(self.config_url) if prefs['debug_mode']: self.webview.setMinimumSize(QSize(600, 300)) else: self.webview.setMinimumSize(QSize(600, 600)) self.global_settings = self.webview.page().settings() #QWebSettings.globalSettings() self.global_settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True) 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.PrivateBrowsingEnabled, True) self.webview.show() self.l.addWidget(self.webview) if prefs['debug_mode']: 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())
class BulkSeries(BulkBase): def setup_ui(self, parent): self.make_widgets(parent, EditWithComplete) values = self.all_values = list(self.db.all_custom(num=self.col_id)) values.sort(key=sort_key) self.main_widget.setSizeAdjustPolicy(self.main_widget.AdjustToMinimumContentsLengthWithIcon) self.main_widget.setMinimumContentsLength(25) self.widgets.append(QLabel('', parent)) w = QWidget(parent) layout = QHBoxLayout(w) layout.setContentsMargins(0, 0, 0, 0) self.remove_series = QCheckBox(parent) self.remove_series.setText(_('Remove series')) layout.addWidget(self.remove_series) self.idx_widget = QCheckBox(parent) self.idx_widget.setText(_('Automatically number books')) layout.addWidget(self.idx_widget) self.force_number = QCheckBox(parent) self.force_number.setText(_('Force numbers to start with ')) layout.addWidget(self.force_number) self.series_start_number = QSpinBox(parent) self.series_start_number.setMinimum(1) self.series_start_number.setMaximum(9999999) self.series_start_number.setProperty("value", 1) layout.addWidget(self.series_start_number) layout.addItem(QSpacerItem(20, 10, QSizePolicy.Expanding, QSizePolicy.Minimum)) self.widgets.append(w) self.idx_widget.stateChanged.connect(self.check_changed_checkbox) self.force_number.stateChanged.connect(self.check_changed_checkbox) self.series_start_number.valueChanged.connect(self.check_changed_checkbox) self.remove_series.stateChanged.connect(self.check_changed_checkbox) self.ignore_change_signals = False def check_changed_checkbox(self): self.a_c_checkbox.setChecked(True) def initialize(self, book_id): self.idx_widget.setChecked(False) self.main_widget.set_separator(None) self.main_widget.update_items_cache(self.all_values) self.main_widget.setEditText('') self.a_c_checkbox.setChecked(False) def getter(self): n = unicode(self.main_widget.currentText()).strip() i = self.idx_widget.checkState() f = self.force_number.checkState() s = self.series_start_number.value() r = self.remove_series.checkState() return n, i, f, s, r def commit(self, book_ids, notify=False): if not self.a_c_checkbox.isChecked(): return val, update_indices, force_start, at_value, clear = self.gui_val val = None if clear else self.normalize_ui_val(val) if clear or val != '': extras = [] for book_id in book_ids: if clear: extras.append(None) continue if update_indices: if force_start: s_index = at_value at_value += 1 elif tweaks['series_index_auto_increment'] != 'const': s_index = self.db.get_next_cc_series_num_for(val, num=self.col_id) else: s_index = 1.0 else: s_index = self.db.get_custom_extra(book_id, num=self.col_id, index_is_id=True) extras.append(s_index) self.db.set_custom_bulk(book_ids, val, extras=extras, num=self.col_id, notify=notify)
def __init__(self, fm, pref_name, parent=None): QDialog.__init__(self, parent) self.fm = fm if pref_name == 'column_color_rules': self.rule_kind = 'color' rule_text = _('column coloring') elif pref_name == 'column_icon_rules': self.rule_kind = 'icon' rule_text = _('column icon') elif pref_name == 'cover_grid_icon_rules': self.rule_kind = 'emblem' rule_text = _('Cover grid emblem') self.setWindowIcon(QIcon(I('format-fill-color.png'))) self.setWindowTitle(_('Create/edit a {0} rule').format(rule_text)) self.l = l = QGridLayout(self) self.setLayout(l) self.l1 = l1 = QLabel(_('Create a {0} rule by' ' filling in the boxes below').format(rule_text)) l.addWidget(l1, 0, 0, 1, 8) self.f1 = QFrame(self) self.f1.setFrameShape(QFrame.HLine) l.addWidget(self.f1, 1, 0, 1, 8) self.l2 = l2 = QLabel(_('Add the emblem:') if self.rule_kind == 'emblem' else _('Set the')) l.addWidget(l2, 2, 0) if self.rule_kind == 'color': l.addWidget(QLabel(_('color'))) elif self.rule_kind == 'icon': self.kind_box = QComboBox(self) for tt, t in icon_rule_kinds: self.kind_box.addItem(tt, t) l.addWidget(self.kind_box, 2, 1) self.kind_box.setToolTip(textwrap.fill(_( 'If you choose composed icons and multiple rules match, then all the' ' matching icons will be combined, otherwise the icon from the' ' first rule to match will be used.'))) else: pass self.l3 = l3 = QLabel(_('of the column:')) l.addWidget(l3, 2, 2) self.column_box = QComboBox(self) l.addWidget(self.column_box, 2, 3) self.l4 = l4 = QLabel(_('to')) l.addWidget(l4, 2, 4) if self.rule_kind == 'emblem': l3.setVisible(False), self.column_box.setVisible(False), l4.setVisible(False) def create_filename_box(): self.filename_box = f = QComboBox() self.filenamebox_view = v = QListView() v.setIconSize(QSize(32, 32)) self.filename_box.setView(v) self.orig_filenamebox_view = f.view() f.setMinimumContentsLength(20), f.setSizeAdjustPolicy(f.AdjustToMinimumContentsLengthWithIcon) self.populate_icon_filenames() if self.rule_kind == 'color': self.color_box = ColorButton(parent=self) self.color_label = QLabel('Sample text Sample text') self.color_label.setTextFormat(Qt.RichText) l.addWidget(self.color_box, 2, 5) l.addWidget(self.color_label, 2, 6) l.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding), 2, 7) elif self.rule_kind == 'emblem': create_filename_box() self.update_filename_box() self.filename_button = QPushButton(QIcon(I('document_open.png')), _('&Add new image')) l.addWidget(self.filename_box) l.addWidget(self.filename_button, 2, 6) l.addWidget(QLabel(_('(Images should be square-ish)')), 2, 7) l.setColumnStretch(7, 10) else: create_filename_box() vb = QVBoxLayout() self.multiple_icon_cb = QCheckBox(_('Choose &more than one icon')) vb.addWidget(self.multiple_icon_cb) self.update_filename_box() self.multiple_icon_cb.clicked.connect(self.multiple_box_clicked) vb.addWidget(self.filename_box) l.addLayout(vb, 2, 5) self.filename_button = QPushButton(QIcon(I('document_open.png')), _('&Add icon')) l.addWidget(self.filename_button, 2, 6) l.addWidget(QLabel(_('Icons should be square or landscape')), 2, 7) l.setColumnStretch(7, 10) self.l5 = l5 = QLabel( _('Only if the following conditions are all satisfied:')) l.addWidget(l5, 3, 0, 1, 7) self.scroll_area = sa = QScrollArea(self) sa.setMinimumHeight(300) sa.setMinimumWidth(950) sa.setWidgetResizable(True) l.addWidget(sa, 4, 0, 1, 8) self.add_button = b = QPushButton(QIcon(I('plus.png')), _('Add &another condition')) l.addWidget(b, 5, 0, 1, 8) b.clicked.connect(self.add_blank_condition) self.l6 = l6 = QLabel(_('You can disable a condition by' ' blanking all of its boxes')) l.addWidget(l6, 6, 0, 1, 8) self.bb = bb = QDialogButtonBox( QDialogButtonBox.Ok|QDialogButtonBox.Cancel) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) l.addWidget(bb, 7, 0, 1, 8) if self.rule_kind != 'color': self.remove_button = b = bb.addButton(_('&Remove icon'), bb.ActionRole) b.setIcon(QIcon(I('minus.png'))) b.setMenu(QMenu()) b.setToolTip('<p>' + _('Remove a previously added icon. Note that doing so will cause rules that use it to stop working.')) self.update_remove_button() self.conditions_widget = QWidget(self) sa.setWidget(self.conditions_widget) self.conditions_widget.setLayout(QVBoxLayout()) self.conditions_widget.layout().setAlignment(Qt.AlignTop) self.conditions = [] if self.rule_kind == 'color': for b in (self.column_box, ): b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon) b.setMinimumContentsLength(15) for key in sorted(displayable_columns(fm), key=lambda k: sort_key(fm[k]['name']) if k != color_row_key else b''): if key == color_row_key and self.rule_kind != 'color': continue name = all_columns_string if key == color_row_key else fm[key]['name'] if name: self.column_box.addItem(name, key) self.column_box.setCurrentIndex(0) if self.rule_kind == 'color': self.color_box.color = '#000' self.update_color_label() self.color_box.color_changed.connect(self.update_color_label) else: self.rule_icon_files = [] self.filename_button.clicked.connect(self.filename_button_clicked) self.resize(self.sizeHint())
class Config(QDialog): ''' Configuration dialog for single book conversion. If accepted, has the following important attributes output_format - Output format (without a leading .) input_format - Input format (without a leading .) opf_path - Path to OPF file with user specified metadata cover_path - Path to user specified cover (can be None) recommendations - A pickled list of 3 tuples in the same format as the recommendations member of the Input/Output plugins. ''' def __init__(self, parent, db, book_id, preferred_input_format=None, preferred_output_format=None): QDialog.__init__(self, parent) self.widgets = [] self.setupUi() self.opt_individual_saved_settings.setVisible(False) self.db, self.book_id = db, book_id self.setup_input_output_formats(self.db, self.book_id, preferred_input_format, preferred_output_format) self.setup_pipeline() self.input_formats.currentIndexChanged[native_string_type].connect( self.setup_pipeline) self.output_formats.currentIndexChanged[native_string_type].connect( self.setup_pipeline) self.groups.setSpacing(5) self.groups.activated[(QModelIndex)].connect(self.show_pane) self.groups.clicked[(QModelIndex)].connect(self.show_pane) self.groups.entered[(QModelIndex)].connect(self.show_group_help) rb = self.buttonBox.button(self.buttonBox.RestoreDefaults) rb.setText(_('Restore &defaults')) rb.clicked.connect(self.restore_defaults) self.groups.setMouseTracking(True) geom = gprefs.get('convert_single_dialog_geom', None) if geom: QApplication.instance().safe_restore_geometry(self, geom) else: self.resize(self.sizeHint()) def setupUi(self): self.setObjectName("Dialog") self.resize(1024, 700) self.setWindowIcon(QIcon(I('convert.png'))) self.gridLayout = QGridLayout(self) self.gridLayout.setObjectName("gridLayout") self.horizontalLayout = QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") self.input_label = QLabel(self) self.input_label.setObjectName("input_label") self.horizontalLayout.addWidget(self.input_label) self.input_formats = QComboBox(self) self.input_formats.setSizeAdjustPolicy( QComboBox.AdjustToMinimumContentsLengthWithIcon) self.input_formats.setMinimumContentsLength(5) self.input_formats.setObjectName("input_formats") self.horizontalLayout.addWidget(self.input_formats) self.opt_individual_saved_settings = QCheckBox(self) self.opt_individual_saved_settings.setObjectName( "opt_individual_saved_settings") self.horizontalLayout.addWidget(self.opt_individual_saved_settings) spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout.addItem(spacerItem) self.label_2 = QLabel(self) self.label_2.setObjectName("label_2") self.horizontalLayout.addWidget(self.label_2) self.output_formats = QComboBox(self) self.output_formats.setSizeAdjustPolicy( QComboBox.AdjustToMinimumContentsLengthWithIcon) self.output_formats.setMinimumContentsLength(5) self.output_formats.setObjectName("output_formats") self.horizontalLayout.addWidget(self.output_formats) self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 2) self.groups = QListView(self) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(1) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.groups.sizePolicy().hasHeightForWidth()) self.groups.setSizePolicy(sizePolicy) self.groups.setTabKeyNavigation(True) self.groups.setIconSize(QSize(48, 48)) self.groups.setWordWrap(True) self.groups.setObjectName("groups") self.gridLayout.addWidget(self.groups, 1, 0, 3, 1) self.scrollArea = QScrollArea(self) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(4) sizePolicy.setVerticalStretch(10) sizePolicy.setHeightForWidth( self.scrollArea.sizePolicy().hasHeightForWidth()) self.scrollArea.setSizePolicy(sizePolicy) self.scrollArea.setFrameShape(QFrame.NoFrame) self.scrollArea.setLineWidth(0) self.scrollArea.setWidgetResizable(True) self.scrollArea.setObjectName("scrollArea") self.page = QWidget() self.page.setObjectName("page") self.gridLayout.addWidget(self.scrollArea, 1, 1, 1, 1) self.buttonBox = QDialogButtonBox(self) self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok | QDialogButtonBox.RestoreDefaults) self.buttonBox.setObjectName("buttonBox") self.gridLayout.addWidget(self.buttonBox, 3, 1, 1, 1) self.help = QTextEdit(self) self.help.setReadOnly(True) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.help.sizePolicy().hasHeightForWidth()) self.help.setSizePolicy(sizePolicy) self.help.setMaximumHeight(80) self.help.setObjectName("help") self.gridLayout.addWidget(self.help, 2, 1, 1, 1) self.input_label.setBuddy(self.input_formats) self.label_2.setBuddy(self.output_formats) self.input_label.setText(_("&Input format:")) self.opt_individual_saved_settings.setText( _("Use &saved conversion settings for individual books")) self.label_2.setText(_("&Output format:")) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) def sizeHint(self): desktop = QCoreApplication.instance().desktop() geom = desktop.availableGeometry(self) nh, nw = max(300, geom.height() - 100), max(400, geom.width() - 70) return QSize(nw, nh) def restore_defaults(self): delete_specifics(self.db, self.book_id) self.setup_pipeline() @property def input_format(self): return unicode_type(self.input_formats.currentText()).lower() @property def output_format(self): return unicode_type(self.output_formats.currentText()).lower() @property def manually_fine_tune_toc(self): for w in self.widgets: if hasattr(w, 'manually_fine_tune_toc'): return w.manually_fine_tune_toc.isChecked() def setup_pipeline(self, *args): oidx = self.groups.currentIndex().row() input_format = self.input_format output_format = self.output_format self.plumber = create_dummy_plumber(input_format, output_format) def widget_factory(cls): return cls(self, self.plumber.get_option_by_name, self.plumber.get_option_help, self.db, self.book_id) self.mw = widget_factory(MetadataWidget) self.setWindowTitle( _('Convert') + ' ' + unicode_type(self.mw.title.text())) lf = widget_factory(LookAndFeelWidget) hw = widget_factory(HeuristicsWidget) sr = widget_factory(SearchAndReplaceWidget) ps = widget_factory(PageSetupWidget) sd = widget_factory(StructureDetectionWidget) toc = widget_factory(TOCWidget) from calibre.gui2.actions.toc_edit import SUPPORTED toc.manually_fine_tune_toc.setVisible( output_format.upper() in SUPPORTED) debug = widget_factory(DebugWidget) output_widget = self.plumber.output_plugin.gui_configuration_widget( self, self.plumber.get_option_by_name, self.plumber.get_option_help, self.db, self.book_id) input_widget = self.plumber.input_plugin.gui_configuration_widget( self, self.plumber.get_option_by_name, self.plumber.get_option_help, self.db, self.book_id) self.break_cycles() self.widgets = widgets = [self.mw, lf, hw, ps, sd, toc, sr] if input_widget is not None: widgets.append(input_widget) if output_widget is not None: widgets.append(output_widget) widgets.append(debug) for w in widgets: w.set_help_signal.connect(self.help.setPlainText) w.setVisible(False) self._groups_model = GroupModel(widgets) self.groups.setModel(self._groups_model) idx = oidx if -1 < oidx < self._groups_model.rowCount() else 0 self.groups.setCurrentIndex(self._groups_model.index(idx)) self.show_pane(idx) try: shutil.rmtree(self.plumber.archive_input_tdir, ignore_errors=True) except Exception: pass def setup_input_output_formats(self, db, book_id, preferred_input_format, preferred_output_format): if preferred_output_format: preferred_output_format = preferred_output_format.upper() output_formats = get_output_formats(preferred_output_format) input_format, input_formats = get_input_format_for_book( db, book_id, preferred_input_format) preferred_output_format = preferred_output_format if \ preferred_output_format in output_formats else \ sort_formats_by_preference(output_formats, [prefs['output_format']])[0] self.input_formats.addItems( (unicode_type(x.upper()) for x in input_formats)) self.output_formats.addItems( (unicode_type(x.upper()) for x in output_formats)) self.input_formats.setCurrentIndex(input_formats.index(input_format)) self.output_formats.setCurrentIndex( output_formats.index(preferred_output_format)) def show_pane(self, index): if hasattr(index, 'row'): index = index.row() ow = self.scrollArea.takeWidget() if ow: ow.setParent(self) for i, w in enumerate(self.widgets): if i == index: self.scrollArea.setWidget(w) w.show() else: w.setVisible(False) def accept(self): recs = GuiRecommendations() for w in self._groups_model.widgets: if not w.pre_commit_check(): return x = w.commit(save_defaults=False) recs.update(x) self.opf_file, self.cover_file = self.mw.opf_file, self.mw.cover_file self._recommendations = recs if self.db is not None: recs['gui_preferred_input_format'] = self.input_format save_specifics(self.db, self.book_id, recs) self.break_cycles() QDialog.accept(self) def reject(self): self.break_cycles() QDialog.reject(self) def done(self, r): if self.isVisible(): gprefs['convert_single_dialog_geom'] = \ bytearray(self.saveGeometry()) return QDialog.done(self, r) def break_cycles(self): for w in self.widgets: w.break_cycles() @property def recommendations(self): recs = [(k, v, OptionRecommendation.HIGH) for k, v in self._recommendations.items()] return recs def show_group_help(self, index): widget = self._groups_model.widgets[index.row()] self.help.setPlainText(widget.HELP)
def setupUi(self): self.setObjectName("Dialog") self.resize(1024, 700) self.setWindowIcon(QIcon(I('convert.png'))) self.gridLayout = QGridLayout(self) self.gridLayout.setObjectName("gridLayout") self.horizontalLayout = QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") self.input_label = QLabel(self) self.input_label.setObjectName("input_label") self.horizontalLayout.addWidget(self.input_label) self.input_formats = QComboBox(self) self.input_formats.setSizeAdjustPolicy( QComboBox.AdjustToMinimumContentsLengthWithIcon) self.input_formats.setMinimumContentsLength(5) self.input_formats.setObjectName("input_formats") self.horizontalLayout.addWidget(self.input_formats) self.opt_individual_saved_settings = QCheckBox(self) self.opt_individual_saved_settings.setObjectName( "opt_individual_saved_settings") self.horizontalLayout.addWidget(self.opt_individual_saved_settings) spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout.addItem(spacerItem) self.label_2 = QLabel(self) self.label_2.setObjectName("label_2") self.horizontalLayout.addWidget(self.label_2) self.output_formats = QComboBox(self) self.output_formats.setSizeAdjustPolicy( QComboBox.AdjustToMinimumContentsLengthWithIcon) self.output_formats.setMinimumContentsLength(5) self.output_formats.setObjectName("output_formats") self.horizontalLayout.addWidget(self.output_formats) self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 2) self.groups = QListView(self) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(1) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.groups.sizePolicy().hasHeightForWidth()) self.groups.setSizePolicy(sizePolicy) self.groups.setTabKeyNavigation(True) self.groups.setIconSize(QSize(48, 48)) self.groups.setWordWrap(True) self.groups.setObjectName("groups") self.gridLayout.addWidget(self.groups, 1, 0, 3, 1) self.scrollArea = QScrollArea(self) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(4) sizePolicy.setVerticalStretch(10) sizePolicy.setHeightForWidth( self.scrollArea.sizePolicy().hasHeightForWidth()) self.scrollArea.setSizePolicy(sizePolicy) self.scrollArea.setFrameShape(QFrame.NoFrame) self.scrollArea.setLineWidth(0) self.scrollArea.setWidgetResizable(True) self.scrollArea.setObjectName("scrollArea") self.page = QWidget() self.page.setObjectName("page") self.gridLayout.addWidget(self.scrollArea, 1, 1, 1, 1) self.buttonBox = QDialogButtonBox(self) self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok | QDialogButtonBox.RestoreDefaults) self.buttonBox.setObjectName("buttonBox") self.gridLayout.addWidget(self.buttonBox, 3, 1, 1, 1) self.help = QTextEdit(self) self.help.setReadOnly(True) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.help.sizePolicy().hasHeightForWidth()) self.help.setSizePolicy(sizePolicy) self.help.setMaximumHeight(80) self.help.setObjectName("help") self.gridLayout.addWidget(self.help, 2, 1, 1, 1) self.input_label.setBuddy(self.input_formats) self.label_2.setBuddy(self.output_formats) self.input_label.setText(_("&Input format:")) self.opt_individual_saved_settings.setText( _("Use &saved conversion settings for individual books")) self.label_2.setText(_("&Output format:")) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject)
def ask_about_cc_mismatch(gui, db, newdb, missing_cols, incompatible_cols): # {{{ source_metadata = db.field_metadata.custom_field_metadata(include_composites=True) dest_library_path = newdb.library_path ndbname = os.path.basename(dest_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: changes_made = False 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']) changes_made = True if changes_made: # Unload the db so that the changes are available # when it is next accessed from calibre.gui2.ui import get_gui library_broker = get_gui().library_broker library_broker.unload_library(dest_library_path) return True return False
class MainWindow(QWidget): """Main window class that provides logic of the program. Allows to search concept and to show information about concepts and relations """ def __init__(self, application): super().__init__() self.application = application self.db = self.openDatabase() self._initUI() def initActions(self): """initalization of actions for main windows""" self.help_action = QAction("&Help", self) self.help_action.setShortcut("F1") self.help_action.setStatusTip('Help') self.help_action.triggered.connect(lambda: HelpDialog(self)) self.new_db_action = QAction("New DB", self) self.new_db_action.setShortcut("Ctrl+N") self.new_db_action.triggered.connect(self.openNewDB) self.delete_сoncept_action = QAction("&Delete", self) self.delete_сoncept_action.setShortcut("DEL") self.delete_сoncept_action.setStatusTip("Delete") self.delete_сoncept_action.triggered.connect(lambda: self.delete(Concept)) self.delete_subcategory_action = QAction("&Delete subcategory", self) self.delete_subcategory_action.triggered.connect(lambda: self.delete(Subcategory)) self.edit_concept_action = QAction("&Edit", self) self.edit_concept_action.setShortcut("F2") self.edit_concept_action.triggered.connect(self.editConcept) self.edit_relation_action = QAction("&Edit", self) self.edit_relation_action.setShortcut("Ctrl+F2") self.edit_relation_action.triggered.connect(self.editRelation) self.exit_action = QAction("&Exit", self) self.exit_action.setShortcut("ESC") self.exit_action.triggered.connect(self.close) self.select_relation_action = QAction("&Select relation", self) self.select_relation_action.triggered.connect(self.setRelationDescription) self.delete_relation_action = QAction("&Delete", self) self.delete_relation_action.setShortcut("Ctrl+DEL") self.delete_relation_action.triggered.connect(lambda: self.delete(Relation)) def openNewDB(self): """This method deletes setting.py file and closes current application session. This uses for creating a new instance of Main Window. """ if os.path.exists("setting.py"): os.remove("setting.py") self.application.exit(1) def _initUI(self): self.setWindowIcon(QIcon("logo.png")) self.setWindowTitle('GraphNotes') self.resize(1000, 700) self.initActions() self.add_data_button = QPushButton("Add data", self) self.search_box = QGroupBox() self._initSearchFrame(self.search_box) self.concept_list = QListWidget(self) self.relation_list = QListWidget(self) self.result_table = QTextEdit(self) self.concept_list.itemClicked.connect(self.searchRelations) self.concept_list.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.concept_list.setContextMenuPolicy(Qt.CustomContextMenu) self.concept_list.customContextMenuRequested.connect(self._showConceptContextMenu) self.relation_list.setContextMenuPolicy(Qt.CustomContextMenu) self.relation_list.customContextMenuRequested.connect(self._showRelationContextMenu) self.relation_list.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.relation_list.itemClicked.connect(self.setRelationDescription) self.grid = QGridLayout() self.result_table.setReadOnly(True) self.search() self._configWidgets() self._bindWidgets() self.show() def _showConceptContextMenu(self, pos): global_position = self.concept_list.mapToGlobal(pos) my_menu = QMenu() my_menu.addAction(self.edit_concept_action) my_menu.addAction(self.delete_сoncept_action) my_menu.addAction(self.delete_subcategory_action) my_menu.addSeparator() my_menu.addAction(self.help_action) my_menu.addAction(self.exit_action) my_menu.exec(global_position) def _initSearchFrame(self, frame): self.search_line = QLineEdit(frame) checkboxes = QGroupBox() checkboxes.setStyleSheet("border:0;") self.concept_checkbox = QCheckBox("Concept", checkboxes) self.description_checkbox = QCheckBox("Description", checkboxes) self.study_checkbox = QCheckBox("Study", checkboxes) self.search_button = QPushButton("Search", checkboxes) checkboxes_layout = QGridLayout(checkboxes) checkboxes_layout.addWidget(self.concept_checkbox, 0, 0) checkboxes_layout.addWidget(self.description_checkbox, 0, 1) checkboxes_layout.addWidget(self.study_checkbox, 0, 2) checkboxes_layout.setContentsMargins(0, 0, 0, 0) self.concept_checkbox.setChecked(True) grid = QGridLayout(frame) grid.addWidget(self.search_line, 1, 0) grid.addWidget(self.search_button, 1, 1) grid.addWidget(checkboxes, 2, 0, Qt.AlignLeft) frame.setLayout(grid) def getSearchData(self): """This method reads parameters of search line and checkboxes, and returns "Subcategory" class. None and [] uses for showing checkboxes that have been checked. """ concept = self.search_line.text() if self.concept_checkbox.isChecked() else None description = [Description(text=self.search_line.text())] if self.description_checkbox.isChecked() else [] study = self.search_line.text() if self.study_checkbox.isChecked() else None return Subcategory(subcategory=concept, description=description, study=study) def searchRelations(self): """This method triggers when the user clicked on the concept. This method selected ID of a subcategory from selected item in conceptions list and then searches and setting all information about this selected concept. """ subcategory_id = self.concept_list.selectedItems()[0].data(Qt.UserRole)[1].id self.setConceptDescription() result = self.db.search_relation(subcategory_id) self.setResult(result, self.relation_list) def _showRelationContextMenu(self, pos): global_position = self.relation_list.mapToGlobal(pos) my_menu = QMenu() my_menu.addAction(self.edit_relation_action) my_menu.addAction(self.delete_relation_action) my_menu.addSeparator() my_menu.exec(global_position) def editRelation(self): """This method opens dialog for editing Relation""" if self.relation_list.currentIndex().isValid(): relation = self.relation_list.selectedItems()[0].data(Qt.UserRole)[2] dlg = EditRelationDialog(self, relation) if dlg.exec_(): relation = dlg.getValue() self.db.update_relation(relation) self.select_relation_action.trigger() def editConcept(self): """This method opens dialog for editing Concept""" if self.concept_list.currentIndex().isValid(): concept = self.concept_list.selectedItems()[0].data(Qt.UserRole)[0] subcategory = self.concept_list.selectedItems()[0].data(Qt.UserRole)[1] dlg = EditConceptDialog(self, concept, subcategory) if dlg.exec_(): concept, subcategory = dlg.getValue() self.db.update_concept(concept) self.db.update_subcategory(subcategory) self.search() def delete(self, something): """This universal method for deleting something from the list and database. "something" parameter needs to be any of this types: Concept, Subcategory, Relation """ if something == Concept: number = 0 target_list = self.concept_list elif something == Subcategory: number = 1 target_list = self.concept_list elif something == Relation: number = 2 target_list = self.relation_list if target_list.currentIndex().isValid(): something = target_list.selectedItems()[0].data(Qt.UserRole)[number] self.db.delete(something) self.search() def setRelationDescription(self): """This method retrieves information about relation, formats and sets it in description field. """ if self.relation_list.currentIndex().isValid(): relation = self.relation_list.selectedItems()[0].data(Qt.UserRole)[2] concept1 = "{}{}".format(relation.node1.concept.name, ", {}".format(relation.node1.subcategory) if relation.node1.subcategory else "") concept2 = "{}{}".format(relation.node2.concept.name, ", {}".format(relation.node2.subcategory) if relation.node2.subcategory else "") description = relation.description study = relation.study references = relation.reference # HTML is used for a better information formating text = "<b>Relation between </b> \"{}\" <b>and</b> \"{}\"<p>".format(concept1, concept2) text += r"<b>Description:</b>" + "<br> {} <p>".format(description) text += r"<b>Study:</b> {} <p>".format(study) text += r"<b>References:</b><ol>" for ref in references: text += " <li> {} </li>".format(ref.text) text += "</ol><br>" self.result_table.setText(text) else: self.result_table.clear() def setConceptDescription(self): """This method retrieves information about concept, formats and sets it in description field. """ concept = self.concept_list.selectedItems()[0].data(Qt.UserRole)[0] subcategory = self.concept_list.selectedItems()[0].data(Qt.UserRole)[1] description = subcategory.description # HTML is used for a better information formating text = "<b>Concept:</b> {}<p>".format(concept.name) text += "<b>Subcategory:</b> {}<p>".format(subcategory.subcategory) text += "<b>Synonyms:</b> {}<p>".format(concept.synonyms) text += "<ol>" for des in description: text += "<li> {}".format("<b>Description:</b> <br>") text += " {} <p>".format(des.text) text += "<ol>" text += "<b>References: </b>" for ref in des.reference: text += "<li>" text += "{}".format(ref.text) text += "</li>" text += "</ol>" text += "</li>" text += "</ol>" self.result_table.setText(text) def setResult(self, result, list): """This is a universal method for showing information in a relation field or in a concept field. """ list.clear() for concept, subcategory, *other in result: item = QListWidgetItem() item.setData(Qt.UserRole, (concept, subcategory, *other)) item.setText("{}{} {}".format(concept.name, "" if subcategory.subcategory == "" else ", ", subcategory.subcategory)) list.insertItem(0, item) def search(self): parameters = self.getSearchData() result = self.db.search_nodes(parameters) self.setResult([], self.relation_list) self.setResult(result, self.concept_list) self.setResult([], self.relation_list) self.setRelationDescription() def _configWidgets(self): self.grid.setAlignment(Qt.AlignTop) self.grid.setSpacing(5) self.menu_bar = QMenuBar(self) self.database_menu = self.menu_bar.addMenu("Database") self.database_menu.addAction(self.new_db_action) self.database_menu.addSeparator() self.database_menu.addAction(self.exit_action) self.concept_menu = self.menu_bar.addMenu("Concept") self.concept_menu.addAction(self.edit_concept_action) self.concept_menu.addAction(self.delete_сoncept_action) self.concept_menu.addAction(self.delete_subcategory_action) self.relation_menu = self.menu_bar.addMenu("Relation") self.relation_menu.addAction(self.edit_relation_action) self.relation_menu.addAction(self.delete_relation_action) self.help_menu = self.menu_bar.addMenu("Help") self.help_menu.addAction(self.help_action) self.grid.setMenuBar(self.menu_bar) self.concept_box = QGroupBox("Concepts") self.concept_grid = QGridLayout() self.concept_grid.setContentsMargins(0, 10, 0, 0) self.concept_grid.addWidget(self.concept_list) self.concept_box.setLayout(self.concept_grid) self.relation_box = QGroupBox("Relations") self.relation_grid = QGridLayout() self.relation_grid.setContentsMargins(0, 10, 0, 0) self.relation_grid.addWidget(self.relation_list) self.relation_box.setLayout(self.relation_grid) self.description_box = QGroupBox("Description") self.description_grid = QGridLayout() self.description_grid.setContentsMargins(0, 10, 0, 0) self.description_grid.addWidget(self.result_table) self.description_box.setLayout(self.description_grid) self.concept_splitter = QSplitter(self) self.concept_splitter.addWidget(self.concept_box) self.concept_splitter.addWidget(self.relation_box) self.description_splitter = QSplitter(self) self.description_splitter.setOrientation(Qt.Vertical) self.description_splitter.addWidget(self.concept_splitter) self.description_splitter.addWidget(self.description_box) self.grid.addWidget(self.add_data_button, 1, 0, Qt.AlignLeft) self.grid.addWidget(self.search_box, 2, 0, 1, 2) self.grid.addWidget(self.description_splitter, 5, 0, 1, 2) self.setLayout(self.grid) def _bindWidgets(self): self.add_data_button.clicked.connect(self.addData) self.search_button.clicked.connect(self.search) self.search_line.returnPressed.connect(self.search) def addData(self): dlg = NewDataDialog(self.db) dlg.exec_() self.search() def getPath(self): dialog = NewDBDialog() if dialog.exec_(): print("EXEc") path = dialog.path with open("setting.py", "w") as f: f.write("{{ 'last': r'{0}' }}".format(path)) return path else: self.close() def openDatabase(self): if os.path.exists("setting.py"): with open("setting.py") as f: setting = eval(f.read()) if "last" in setting: if os.path.exists(setting["last"]): path = setting["last"] else: path = self.getPath() else: path = self.getPath() else: path = self.getPath() if path is None: self.close() exit(0) else: return Graph(path)
def __init__(self, parent, db, author, series=None, title=None, dup_title=None): QDialog.__init__(self, parent) self.db = db self.setWindowTitle(_('How many empty books?')) self._layout = QGridLayout(self) self.setLayout(self._layout) self.qty_label = QLabel(_('How many empty books should be added?')) self._layout.addWidget(self.qty_label, 0, 0, 1, 2) self.qty_spinbox = QSpinBox(self) self.qty_spinbox.setRange(1, 10000) self.qty_spinbox.setValue(1) self._layout.addWidget(self.qty_spinbox, 1, 0, 1, 2) self.author_label = QLabel(_('Set the author of the new books to:')) self._layout.addWidget(self.author_label, 2, 0, 1, 2) self.authors_combo = EditWithComplete(self) self.authors_combo.setSizeAdjustPolicy( self.authors_combo.AdjustToMinimumContentsLengthWithIcon) self.authors_combo.setEditable(True) self._layout.addWidget(self.authors_combo, 3, 0, 1, 1) self.initialize_authors(db, author) self.clear_button = QToolButton(self) self.clear_button.setIcon(QIcon(I('trash.png'))) self.clear_button.setToolTip(_('Reset author to Unknown')) self.clear_button.clicked.connect(self.reset_author) self._layout.addWidget(self.clear_button, 3, 1, 1, 1) self.series_label = QLabel(_('Set the series of the new books to:')) self._layout.addWidget(self.series_label, 4, 0, 1, 2) self.series_combo = EditWithComplete(self) self.series_combo.setSizeAdjustPolicy( self.authors_combo.AdjustToMinimumContentsLengthWithIcon) self.series_combo.setEditable(True) self._layout.addWidget(self.series_combo, 5, 0, 1, 1) self.initialize_series(db, series) self.sclear_button = QToolButton(self) self.sclear_button.setIcon(QIcon(I('trash.png'))) self.sclear_button.setToolTip(_('Reset series')) self.sclear_button.clicked.connect(self.reset_series) self._layout.addWidget(self.sclear_button, 5, 1, 1, 1) self.title_label = QLabel(_('Set the title of the new books to:')) self._layout.addWidget(self.title_label, 6, 0, 1, 2) self.title_edit = QLineEdit(self) self.title_edit.setText(title or '') self._layout.addWidget(self.title_edit, 7, 0, 1, 1) self.tclear_button = QToolButton(self) self.tclear_button.setIcon(QIcon(I('trash.png'))) self.tclear_button.setToolTip(_('Reset title')) self.tclear_button.clicked.connect(self.title_edit.clear) self._layout.addWidget(self.tclear_button, 7, 1, 1, 1) self.format_label = QLabel(_('Also create an empty e-book in format:')) self._layout.addWidget(self.format_label, 8, 0, 1, 2) c = self.format_value = QComboBox(self) from calibre.ebooks.oeb.polish.create import valid_empty_formats possible_formats = [''] + sorted(x.upper() for x in valid_empty_formats) c.addItems(possible_formats) c.setToolTip(_('Also create an empty book format file that you can subsequently edit')) if gprefs.get('create_empty_epub_file', False): # Migration of the check box gprefs.set('create_empty_format_file', 'epub') del gprefs['create_empty_epub_file'] use_format = gprefs.get('create_empty_format_file', '').upper() try: c.setCurrentIndex(possible_formats.index(use_format)) except Exception: pass self._layout.addWidget(c, 9, 0, 1, 1) self.copy_formats = cf = QCheckBox(_('Also copy book &formats when duplicating a book'), self) cf.setToolTip(_( 'Also copy all e-book files into the newly created duplicate' ' books.')) cf.setChecked(gprefs.get('create_empty_copy_dup_formats', False)) self._layout.addWidget(cf, 10, 0, 1, -1) button_box = self.bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) button_box.accepted.connect(self.accept) button_box.rejected.connect(self.reject) self._layout.addWidget(button_box, 11, 0, 1, -1) if dup_title: self.dup_button = b = button_box.addButton(_('&Duplicate current book'), button_box.ActionRole) b.clicked.connect(self.do_duplicate_book) b.setIcon(QIcon(I('edit-copy.png'))) b.setToolTip(_( 'Make the new empty book records exact duplicates\n' 'of the current book "%s", with all metadata identical' ) % dup_title) self.resize(self.sizeHint()) self.duplicate_current_book = False
def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.model = None self.setModal(modal) self.setWindowTitle("Restore model into image") lo = QVBoxLayout(self) lo.setContentsMargins(10, 10, 10, 10) lo.setSpacing(5) # file selector self.wfile_in = FileSelector(self, label="Input FITS file:", dialog_label="Input FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.ExistingFile) lo.addWidget(self.wfile_in) self.wfile_out = FileSelector(self, label="Output FITS file:", dialog_label="Output FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.AnyFile) lo.addWidget(self.wfile_out) # beam size lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) lo1.addWidget(QLabel("Restoring beam FWHM, major axis:", self)) self.wbmaj = QLineEdit(self) lo1.addWidget(self.wbmaj) lo1.addWidget(QLabel("\" minor axis:", self)) self.wbmin = QLineEdit(self) lo1.addWidget(self.wbmin) lo1.addWidget(QLabel("\" P.A.:", self)) self.wbpa = QLineEdit(self) lo1.addWidget(self.wbpa) lo1.addWidget(QLabel("\u00B0", self)) for w in self.wbmaj, self.wbmin, self.wbpa: w.setValidator(QDoubleValidator(self)) lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.wfile_psf = FileSelector( self, label="Set restoring beam by fitting PSF image:", dialog_label="PSF FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.ExistingFile) lo1.addSpacing(32) lo1.addWidget(self.wfile_psf) # selection only self.wselonly = QCheckBox("restore selected model sources only", self) lo.addWidget(self.wselonly) # OK/cancel buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(0, 0, 0, 0) lo2.setContentsMargins(5, 5, 5, 5) self.wokbtn = QPushButton("OK", self) self.wokbtn.setMinimumWidth(128) self.wokbtn.clicked.connect(self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) cancelbtn.clicked.connect(self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) # signals self.wfile_in.filenameSelected.connect(self._fileSelected) self.wfile_in.filenameSelected.connect(self._inputFileSelected) self.wfile_out.filenameSelected.connect(self._fileSelected) self.wfile_psf.filenameSelected.connect(self._psfFileSelected) # internal state self.qerrmsg = QErrorMessage(self)
def __init__(self, parent, book_list, get_annotations_as_HTML, source): self.opts = parent.opts self.parent = parent self.get_annotations_as_HTML = get_annotations_as_HTML self.show_confidence_colors = self.opts.prefs.get( 'annotated_books_dialog_show_confidence_as_bg_color', True) self.source = source # QDialog.__init__(self, parent=self.opts.gui) SizePersistedDialog.__init__( self, self.opts.gui, 'Annotations plugin:import annotations dialog') self.setWindowTitle(_('Import Annotations')) self.setWindowIcon(self.opts.icon) self.l = QVBoxLayout(self) self.setLayout(self.l) self.perfect_width = 0 from calibre_plugins.annotations.appearance import default_timestamp friendly_timestamp_format = plugin_prefs.get( 'appearance_timestamp_format', default_timestamp) # Are we collecting News clippings? collect_news_clippings = self.opts.prefs.get( 'cfg_news_clippings_checkbox', False) news_clippings_destination = self.opts.prefs.get( 'cfg_news_clippings_lineEdit', None) # Populate the table data self.tabledata = [] for book_data in book_list: enabled = QCheckBox() enabled.setChecked(True) # last_annotation sorts by timestamp last_annotation = SortableTableWidgetItem( strftime(friendly_timestamp_format, localtime(book_data['last_update'])), book_data['last_update']) # reader_app sorts case-insensitive reader_app = SortableTableWidgetItem( book_data['reader_app'], book_data['reader_app'].upper()) # title, author sort by title_sort, author_sort if not book_data['title_sort']: book_data['title_sort'] = book_data['title'] title = SortableTableWidgetItem(book_data['title'], book_data['title_sort'].upper()) if not book_data['author_sort']: book_data['author_sort'] = book_data['author'] author = SortableTableWidgetItem(book_data['author'], book_data['author_sort'].upper()) genres = book_data['genre'].split(', ') if 'News' in genres and collect_news_clippings: cid = get_clippings_cid(self, news_clippings_destination) confidence = 5 else: cid, confidence = parent.generate_confidence(book_data) # List order matches self.annotations_header this_book = [ book_data['uuid'], book_data['book_id'], book_data['genre'], enabled, reader_app, title, author, last_annotation, book_data['annotations'], confidence ] self.tabledata.append(this_book) self.tv = QTableView(self) self.l.addWidget(self.tv) self.annotations_header = [ 'uuid', 'book_id', 'genre', '', _('Reader App'), _('Title'), _('Author'), _('Last Annotation'), _('Annotations'), _('Confidence') ] self.ENABLED_COL = 3 self.READER_APP_COL = 4 self.TITLE_COL = 5 self.AUTHOR_COL = 6 self.LAST_ANNOTATION_COL = 7 self.CONFIDENCE_COL = 9 columns_to_center = [8] self.tm = MarkupTableModel(self, columns_to_center=columns_to_center) self.tv.setModel(self.tm) self.tv.setShowGrid(False) self.tv.setFont(self.FONT) self.tvSelectionModel = self.tv.selectionModel() self.tv.setAlternatingRowColors(not self.show_confidence_colors) self.tv.setShowGrid(False) self.tv.setWordWrap(False) self.tv.setSelectionBehavior(self.tv.SelectRows) # Connect signals self.tv.doubleClicked.connect(self.getTableRowDoubleClick) self.tv.horizontalHeader().sectionClicked.connect( self.capture_sort_column) # Hide the vertical self.header self.tv.verticalHeader().setVisible(False) # Hide uuid, book_id, genre, confidence self.tv.hideColumn(self.annotations_header.index('uuid')) self.tv.hideColumn(self.annotations_header.index('book_id')) self.tv.hideColumn(self.annotations_header.index('genre')) # self.tv.hideColumn(self.annotations_header.index(_('Confidence'))) self.tv.hideColumn(self.CONFIDENCE_COL) # Set horizontal self.header props self.tv.horizontalHeader().setStretchLastSection(True) narrow_columns = [ _('Last Annotation'), _('Reader App'), _('Annotations') ] extra_width = 10 breathing_space = 20 # Set column width to fit contents self.tv.resizeColumnsToContents() perfect_width = 10 + (len(narrow_columns) * extra_width) for i in range(3, 8): perfect_width += self.tv.columnWidth(i) + breathing_space self.tv.setMinimumSize(perfect_width, 100) self.perfect_width = perfect_width # Add some width to narrow columns for nc in narrow_columns: cw = self.tv.columnWidth(self.annotations_header.index(nc)) self.tv.setColumnWidth(self.annotations_header.index(nc), cw + extra_width) # Set row height fm = QFontMetrics(self.FONT) nrows = len(self.tabledata) for row in xrange(nrows): self.tv.setRowHeight(row, fm.height() + 4) self.tv.setSortingEnabled(True) sort_column = self.opts.prefs.get('annotated_books_dialog_sort_column', self.CONFIDENCE_COL) sort_order = self.opts.prefs.get('annotated_books_dialog_sort_order', Qt.DescendingOrder) self.tv.sortByColumn(sort_column, sort_order) # ~~~~~~~~ Create the ButtonBox ~~~~~~~~ self.dialogButtonBox = QDialogButtonBox(QDialogButtonBox.Cancel | QDialogButtonBox.Help) self.dialogButtonBox.setOrientation(Qt.Horizontal) self.import_button = self.dialogButtonBox.addButton( self.dialogButtonBox.Ok) self.import_button.setText(_('Import Annotations')) # Action buttons self.toggle_checkmarks_button = self.dialogButtonBox.addButton( _('Clear All'), QDialogButtonBox.ActionRole) self.toggle_checkmarks_button.setObjectName('toggle_checkmarks_button') scb_text = _('Show match status') if self.show_confidence_colors: scb_text = _("Hide match status") self.show_confidence_button = self.dialogButtonBox.addButton( scb_text, QDialogButtonBox.ActionRole) self.show_confidence_button.setObjectName('confidence_button') if self.show_confidence_colors: self.show_confidence_button.setIcon( get_icon('images/matches_hide.png')) else: self.show_confidence_button.setIcon( get_icon('images/matches_show.png')) self.preview_button = self.dialogButtonBox.addButton( _('Preview'), QDialogButtonBox.ActionRole) self.preview_button.setObjectName('preview_button') self.dialogButtonBox.clicked.connect( self.show_annotated_books_dialog_clicked) self.l.addWidget(self.dialogButtonBox) # Cause our dialog size to be restored from prefs or created on first usage self.resize_dialog()
def create_checkbox(title, tt, state): cb = QCheckBox(title) cb.setToolTip(wrap_msg(tt)) cb.setChecked(bool(state)) return cb
def __init__(self, ids, get_metadata, field_metadata, parent=None, window_title=None, reject_button_tooltip=None, accept_all_tooltip=None, reject_all_tooltip=None, revert_tooltip=None, intro_msg=None, action_button=None, **kwargs): QDialog.__init__(self, parent) self.l = l = QVBoxLayout() self.setLayout(l) self.setWindowIcon(QIcon(I('auto_author_sort.png'))) self.get_metadata = get_metadata self.ids = list(ids) self.total = len(self.ids) self.accepted = OrderedDict() self.rejected_ids = set() self.window_title = window_title or _('Compare metadata') if intro_msg: self.la = la = QLabel(intro_msg) la.setWordWrap(True) l.addWidget(la) self.compare_widget = CompareSingle(field_metadata, parent=parent, revert_tooltip=revert_tooltip, **kwargs) self.sa = sa = QScrollArea() l.addWidget(sa) sa.setWidget(self.compare_widget) sa.setWidgetResizable(True) self.bb = bb = QDialogButtonBox(QDialogButtonBox.Cancel) bb.button(bb.Cancel).setAutoDefault(False) bb.rejected.connect(self.reject) if self.total > 1: self.aarb = b = bb.addButton(_('&Accept all remaining'), bb.YesRole) b.setIcon(QIcon(I('ok.png'))), b.setAutoDefault(False) if accept_all_tooltip: b.setToolTip(accept_all_tooltip) b.clicked.connect(self.accept_all_remaining) self.rarb = b = bb.addButton(_('Re&ject all remaining'), bb.ActionRole) b.setIcon(QIcon(I('minus.png'))), b.setAutoDefault(False) if reject_all_tooltip: b.setToolTip(reject_all_tooltip) b.clicked.connect(self.reject_all_remaining) self.sb = b = bb.addButton(_('R&eject'), bb.ActionRole) connect_lambda(b.clicked, self, lambda self: self.next_item(False)) b.setIcon(QIcon(I('minus.png'))), b.setAutoDefault(False) if reject_button_tooltip: b.setToolTip(reject_button_tooltip) self.next_action = ac = QAction(self) ac.setShortcut(QKeySequence(Qt.ALT | Qt.Key_Right)) self.addAction(ac) if action_button is not None: self.acb = b = bb.addButton(action_button[0], bb.ActionRole) b.setIcon(QIcon(action_button[1])) self.action_button_action = action_button[2] b.clicked.connect(self.action_button_clicked) self.nb = b = bb.addButton( _('&Next') if self.total > 1 else _('&OK'), bb.ActionRole) if self.total > 1: b.setToolTip( _('Move to next [%s]') % self.next_action.shortcut().toString(QKeySequence.NativeText)) self.next_action.triggered.connect(b.click) b.setIcon(QIcon(I('forward.png' if self.total > 1 else 'ok.png'))) connect_lambda(b.clicked, self, lambda self: self.next_item(True)) b.setDefault(True), b.setAutoDefault(True) self.bbh = h = QHBoxLayout() h.setContentsMargins(0, 0, 0, 0) l.addLayout(h) self.markq = m = QCheckBox(_('&Mark rejected books')) m.setChecked(gprefs['metadata_diff_mark_rejected']) connect_lambda( m.stateChanged[int], self, lambda self: gprefs.set( 'metadata_diff_mark_rejected', self.markq.isChecked())) m.setToolTip( _('Mark rejected books in the book list after this dialog is closed' )) h.addWidget(m), h.addWidget(bb) self.next_item(True) desktop = QApplication.instance().desktop() geom = desktop.availableGeometry(parent or self) width = max(700, min(950, geom.width() - 50)) height = max(650, min(1000, geom.height() - 100)) self.resize(QSize(width, height)) geom = gprefs.get('diff_dialog_geom', None) if geom is not None: self.restoreGeometry(geom) b.setFocus(Qt.OtherFocusReason)
class BulkSeries(BulkBase): def setup_ui(self, parent): self.make_widgets(parent, EditWithComplete) values = self.all_values = list(self.db.all_custom(num=self.col_id)) values.sort(key=sort_key) self.main_widget.setSizeAdjustPolicy( self.main_widget.AdjustToMinimumContentsLengthWithIcon) self.main_widget.setMinimumContentsLength(25) self.widgets.append(QLabel('', parent)) w = QWidget(parent) layout = QHBoxLayout(w) layout.setContentsMargins(0, 0, 0, 0) self.remove_series = QCheckBox(parent) self.remove_series.setText(_('Remove series')) layout.addWidget(self.remove_series) self.idx_widget = QCheckBox(parent) self.idx_widget.setText(_('Automatically number books')) layout.addWidget(self.idx_widget) self.force_number = QCheckBox(parent) self.force_number.setText(_('Force numbers to start with ')) layout.addWidget(self.force_number) self.series_start_number = QSpinBox(parent) self.series_start_number.setMinimum(1) self.series_start_number.setMaximum(9999999) self.series_start_number.setProperty("value", 1) layout.addWidget(self.series_start_number) layout.addItem( QSpacerItem(20, 10, QSizePolicy.Expanding, QSizePolicy.Minimum)) self.widgets.append(w) self.idx_widget.stateChanged.connect(self.check_changed_checkbox) self.force_number.stateChanged.connect(self.check_changed_checkbox) self.series_start_number.valueChanged.connect( self.check_changed_checkbox) self.remove_series.stateChanged.connect(self.check_changed_checkbox) self.ignore_change_signals = False def check_changed_checkbox(self): self.a_c_checkbox.setChecked(True) def initialize(self, book_id): self.idx_widget.setChecked(False) self.main_widget.set_separator(None) self.main_widget.update_items_cache(self.all_values) self.main_widget.setEditText('') self.a_c_checkbox.setChecked(False) def getter(self): n = unicode(self.main_widget.currentText()).strip() i = self.idx_widget.checkState() f = self.force_number.checkState() s = self.series_start_number.value() r = self.remove_series.checkState() return n, i, f, s, r def commit(self, book_ids, notify=False): if not self.a_c_checkbox.isChecked(): return val, update_indices, force_start, at_value, clear = self.gui_val val = None if clear else self.normalize_ui_val(val) if clear or val != '': extras = [] for book_id in book_ids: if clear: extras.append(None) continue if update_indices: if force_start: s_index = at_value at_value += 1 elif tweaks['series_index_auto_increment'] != 'const': s_index = self.db.get_next_cc_series_num_for( val, num=self.col_id) else: s_index = 1.0 else: s_index = self.db.get_custom_extra(book_id, num=self.col_id, index_is_id=True) extras.append(s_index) self.db.set_custom_bulk(book_ids, val, extras=extras, num=self.col_id, notify=notify)
def __init__(self, parent=None): QWidget.__init__(self, parent) self.l = l = QGridLayout(self) self.setLayout(l) self.enabled = c = QCheckBox(self) l.addWidget(c, l.rowCount(), 0, 1, 2) c.setVisible(False) c.stateChanged.connect(self.changed) self.l1 = l1 = QLabel('') l1.setWordWrap(True) l.addWidget(l1, l.rowCount(), 0, 1, 2) self.add_button = QPushButton(QIcon(I('plus.png')), _('&Add rule'), self) self.remove_button = QPushButton(QIcon(I('minus.png')), _('&Remove rule(s)'), self) self.add_button.clicked.connect(self.add_rule) self.remove_button.clicked.connect(self.remove_rule) l.addWidget(self.add_button, l.rowCount(), 0) l.addWidget(self.remove_button, l.rowCount() - 1, 1) self.g = g = QGridLayout() self.rules_view = QListView(self) self.rules_view.doubleClicked.connect(self.edit_rule) self.rules_view.setSelectionMode(self.rules_view.ExtendedSelection) self.rules_view.setAlternatingRowColors(True) self.rtfd = RichTextDelegate(parent=self.rules_view, max_width=400) self.rules_view.setItemDelegate(self.rtfd) g.addWidget(self.rules_view, 0, 0, 2, 1) self.up_button = b = QToolButton(self) b.setIcon(QIcon(I('arrow-up.png'))) b.setToolTip(_('Move the selected rule up')) b.clicked.connect(self.move_up) g.addWidget(b, 0, 1, 1, 1, Qt.AlignTop) self.down_button = b = QToolButton(self) b.setIcon(QIcon(I('arrow-down.png'))) b.setToolTip(_('Move the selected rule down')) b.clicked.connect(self.move_down) g.addWidget(b, 1, 1, 1, 1, Qt.AlignBottom) l.addLayout(g, l.rowCount(), 0, 1, 2) l.setRowStretch(l.rowCount() - 1, 10) self.add_advanced_button = b = QPushButton(QIcon(I('plus.png')), _('Add ad&vanced rule'), self) b.clicked.connect(self.add_advanced) self.hb = hb = QHBoxLayout() l.addLayout(hb, l.rowCount(), 0, 1, 2) hb.addWidget(b) hb.addStretch(10) self.export_button = b = QPushButton(_('E&xport'), self) b.clicked.connect(self.export_rules) b.setToolTip(_('Export these rules to a file')) hb.addWidget(b) self.import_button = b = QPushButton(_('&Import'), self) b.setToolTip(_('Import rules from a file')) b.clicked.connect(self.import_rules) hb.addWidget(b)
class JobError(QDialog): # {{{ WIDTH = 600 do_pop = pyqtSignal() def __init__(self, parent): QDialog.__init__(self, parent) self.setAttribute(Qt.WA_DeleteOnClose, False) self.queue = [] self.do_pop.connect(self.pop, type=Qt.QueuedConnection) self._layout = l = QGridLayout() self.setLayout(l) self.icon = QIcon(I('dialog_error.png')) self.setWindowIcon(self.icon) self.icon_widget = Icon(self) self.icon_widget.set_icon(self.icon) self.msg_label = QLabel('<p> ') self.msg_label.setStyleSheet('QLabel { margin-top: 1ex; }') self.msg_label.setWordWrap(True) self.msg_label.setTextFormat(Qt.RichText) self.det_msg = QPlainTextEdit(self) self.det_msg.setVisible(False) self.bb = QDialogButtonBox(QDialogButtonBox.Close, parent=self) self.bb.accepted.connect(self.accept) self.bb.rejected.connect(self.reject) self.ctc_button = self.bb.addButton(_('&Copy to clipboard'), self.bb.ActionRole) self.ctc_button.clicked.connect(self.copy_to_clipboard) self.retry_button = self.bb.addButton(_('&Retry'), self.bb.ActionRole) self.retry_button.clicked.connect(self.retry) self.retry_func = None self.show_det_msg = _('Show &details') self.hide_det_msg = _('Hide &details') self.det_msg_toggle = self.bb.addButton(self.show_det_msg, self.bb.ActionRole) self.det_msg_toggle.clicked.connect(self.toggle_det_msg) self.det_msg_toggle.setToolTip( _('Show detailed information about this error')) self.suppress = QCheckBox(self) l.addWidget(self.icon_widget, 0, 0, 1, 1) l.addWidget(self.msg_label, 0, 1, 1, 1) l.addWidget(self.det_msg, 1, 0, 1, 2) l.addWidget(self.suppress, 2, 0, 1, 2, Qt.AlignLeft|Qt.AlignBottom) l.addWidget(self.bb, 3, 0, 1, 2, Qt.AlignRight|Qt.AlignBottom) l.setColumnStretch(1, 100) self.setModal(False) self.suppress.setVisible(False) self.do_resize() def retry(self): if self.retry_func is not None: self.accept() self.retry_func() def update_suppress_state(self): self.suppress.setText(ngettext( 'Hide the remaining error message', 'Hide the {} remaining error messages', len(self.queue)).format(len(self.queue))) self.suppress.setVisible(len(self.queue) > 3) self.do_resize() def copy_to_clipboard(self, *args): d = QTextDocument() d.setHtml(self.msg_label.text()) QApplication.clipboard().setText( u'calibre, version %s (%s, embedded-python: %s)\n%s: %s\n\n%s' % (__version__, sys.platform, isfrozen, unicode(self.windowTitle()), unicode(d.toPlainText()), unicode(self.det_msg.toPlainText()))) if hasattr(self, 'ctc_button'): self.ctc_button.setText(_('Copied')) def toggle_det_msg(self, *args): vis = unicode(self.det_msg_toggle.text()) == self.hide_det_msg self.det_msg_toggle.setText(self.show_det_msg if vis else self.hide_det_msg) self.det_msg.setVisible(not vis) self.do_resize() def do_resize(self): h = self.sizeHint().height() self.setMinimumHeight(0) # Needed as this gets set if det_msg is shown # Needed otherwise re-showing the box after showing det_msg causes the box # to not reduce in height self.setMaximumHeight(h) self.resize(QSize(self.WIDTH, h)) def showEvent(self, ev): ret = QDialog.showEvent(self, ev) self.bb.button(self.bb.Close).setFocus(Qt.OtherFocusReason) return ret def show_error(self, title, msg, det_msg=u'', retry_func=None): self.queue.append((title, msg, det_msg, retry_func)) self.update_suppress_state() self.pop() def pop(self): if not self.queue or self.isVisible(): return title, msg, det_msg, retry_func = self.queue.pop(0) self.setWindowTitle(title) self.msg_label.setText(msg) self.det_msg.setPlainText(det_msg) self.det_msg.setVisible(False) self.det_msg_toggle.setText(self.show_det_msg) self.det_msg_toggle.setVisible(True) self.suppress.setChecked(False) self.update_suppress_state() if not det_msg: self.det_msg_toggle.setVisible(False) self.retry_button.setVisible(retry_func is not None) self.retry_func = retry_func self.do_resize() self.show() def done(self, r): if self.suppress.isChecked(): self.queue = [] QDialog.done(self, r) self.do_pop.emit()
class OpdsDialog(QDialog): def __init__(self, gui, icon, do_user_config): QDialog.__init__(self, gui) self.gui = gui self.do_user_config = do_user_config self.db = gui.current_db.new_api # The model for the book list self.model = OpdsBooksModel(None, self.dummy_books(), self.db) self.searchproxymodel = QSortFilterProxyModel(self) self.searchproxymodel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.searchproxymodel.setFilterKeyColumn(-1) self.searchproxymodel.setSourceModel(self.model) self.layout = QGridLayout() self.setLayout(self.layout) self.setWindowTitle('OPDS Client') self.setWindowIcon(icon) labelColumnWidths = [] self.opdsUrlLabel = QLabel('OPDS URL: ') self.layout.addWidget(self.opdsUrlLabel, 0, 0) labelColumnWidths.append( self.layout.itemAtPosition(0, 0).sizeHint().width()) config.convertSingleStringOpdsUrlPreferenceToListOfStringsPreference() self.opdsUrlEditor = QComboBox(self) self.opdsUrlEditor.activated.connect(self.opdsUrlEditorActivated) self.opdsUrlEditor.addItems(prefs['opds_url']) self.opdsUrlEditor.setEditable(True) self.opdsUrlEditor.setInsertPolicy(QComboBox.InsertAtTop) self.layout.addWidget(self.opdsUrlEditor, 0, 1, 1, 3) self.opdsUrlLabel.setBuddy(self.opdsUrlEditor) buttonColumnNumber = 7 buttonColumnWidths = [] self.about_button = QPushButton('About', self) self.about_button.setAutoDefault(False) self.about_button.clicked.connect(self.about) self.layout.addWidget(self.about_button, 0, buttonColumnNumber) buttonColumnWidths.append( self.layout.itemAtPosition(0, buttonColumnNumber).sizeHint().width()) # Initially download the catalogs found in the root catalog of the URL # selected at startup. Fail quietly on failing to open the URL catalogsTuple = self.model.downloadOpdsRootCatalog( self.gui, self.opdsUrlEditor.currentText(), False) print catalogsTuple firstCatalogTitle = catalogsTuple[0] self.currentOpdsCatalogs = catalogsTuple[ 1] # A dictionary of title->feedURL self.opdsCatalogSelectorLabel = QLabel('OPDS Catalog:') self.layout.addWidget(self.opdsCatalogSelectorLabel, 1, 0) labelColumnWidths.append( self.layout.itemAtPosition(1, 0).sizeHint().width()) self.opdsCatalogSelector = QComboBox(self) self.opdsCatalogSelector.setEditable(False) self.opdsCatalogSelectorModel = QStringListModel( self.currentOpdsCatalogs.keys()) self.opdsCatalogSelector.setModel(self.opdsCatalogSelectorModel) self.opdsCatalogSelector.setCurrentText(firstCatalogTitle) self.layout.addWidget(self.opdsCatalogSelector, 1, 1, 1, 3) self.download_opds_button = QPushButton('Download OPDS', self) self.download_opds_button.setAutoDefault(False) self.download_opds_button.clicked.connect(self.download_opds) self.layout.addWidget(self.download_opds_button, 1, buttonColumnNumber) buttonColumnWidths.append( self.layout.itemAtPosition(1, buttonColumnNumber).sizeHint().width()) # Search GUI self.searchEditor = QLineEdit(self) self.searchEditor.returnPressed.connect(self.searchBookList) self.layout.addWidget(self.searchEditor, 2, buttonColumnNumber - 2, 1, 2) self.searchButton = QPushButton('Search', self) self.searchButton.setAutoDefault(False) self.searchButton.clicked.connect(self.searchBookList) self.layout.addWidget(self.searchButton, 2, buttonColumnNumber) buttonColumnWidths.append( self.layout.itemAtPosition(2, buttonColumnNumber).sizeHint().width()) # The main book list self.library_view = QTableView(self) self.library_view.setAlternatingRowColors(True) self.library_view.setModel(self.searchproxymodel) self.library_view.horizontalHeader().setSectionResizeMode( 0, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode( 1, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode( 2, QHeaderView.Stretch) self.library_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.resizeAllLibraryViewLinesToHeaderHeight() self.library_view.resizeColumnsToContents() self.layout.addWidget(self.library_view, 3, 0, 3, buttonColumnNumber + 1) self.hideNewsCheckbox = QCheckBox('Hide Newspapers', self) self.hideNewsCheckbox.clicked.connect(self.setHideNewspapers) self.hideNewsCheckbox.setChecked(prefs['hideNewspapers']) self.layout.addWidget(self.hideNewsCheckbox, 6, 0, 1, 3) self.hideBooksAlreadyInLibraryCheckbox = QCheckBox( 'Hide books already in library', self) self.hideBooksAlreadyInLibraryCheckbox.clicked.connect( self.setHideBooksAlreadyInLibrary) self.hideBooksAlreadyInLibraryCheckbox.setChecked( prefs['hideBooksAlreadyInLibrary']) self.layout.addWidget(self.hideBooksAlreadyInLibraryCheckbox, 7, 0, 1, 3) # Let the checkbox initial state control the filtering self.model.setFilterBooksThatAreNewspapers( self.hideNewsCheckbox.isChecked()) self.model.setFilterBooksThatAreAlreadyInLibrary( self.hideBooksAlreadyInLibraryCheckbox.isChecked()) self.downloadButton = QPushButton('Download selected books', self) self.downloadButton.setAutoDefault(False) self.downloadButton.clicked.connect(self.downloadSelectedBooks) self.layout.addWidget(self.downloadButton, 6, buttonColumnNumber) buttonColumnWidths.append( self.layout.itemAtPosition(6, buttonColumnNumber).sizeHint().width()) self.fixTimestampButton = QPushButton('Fix timestamps of selection', self) self.fixTimestampButton.setAutoDefault(False) self.fixTimestampButton.clicked.connect(self.fixBookTimestamps) self.layout.addWidget(self.fixTimestampButton, 7, buttonColumnNumber) buttonColumnWidths.append( self.layout.itemAtPosition(7, buttonColumnNumber).sizeHint().width()) # Make all columns of the grid layout the same width as the button column buttonColumnWidth = max(buttonColumnWidths) for columnNumber in range(0, buttonColumnNumber): self.layout.setColumnMinimumWidth(columnNumber, buttonColumnWidth) # Make sure the first column isn't wider than the labels it holds labelColumnWidth = max(labelColumnWidths) self.layout.setColumnMinimumWidth(0, labelColumnWidth) self.resize(self.sizeHint()) def opdsUrlEditorActivated(self, text): prefs['opds_url'] = config.saveOpdsUrlCombobox(self.opdsUrlEditor) catalogsTuple = self.model.downloadOpdsRootCatalog( self.gui, self.opdsUrlEditor.currentText(), True) firstCatalogTitle = catalogsTuple[0] self.currentOpdsCatalogs = catalogsTuple[ 1] # A dictionary of title->feedURL self.opdsCatalogSelectorModel.setStringList( self.currentOpdsCatalogs.keys()) self.opdsCatalogSelector.setCurrentText(firstCatalogTitle) def setHideNewspapers(self, checked): prefs['hideNewspapers'] = checked self.model.setFilterBooksThatAreNewspapers(checked) self.resizeAllLibraryViewLinesToHeaderHeight() def setHideBooksAlreadyInLibrary(self, checked): prefs['hideBooksAlreadyInLibrary'] = checked self.model.setFilterBooksThatAreAlreadyInLibrary(checked) self.resizeAllLibraryViewLinesToHeaderHeight() def searchBookList(self): searchString = self.searchEditor.text() print "starting book list search for: %s" % searchString self.searchproxymodel.setFilterFixedString(searchString) def about(self): text = get_resources('about.txt') QMessageBox.about(self, 'About the OPDS Client plugin', text.decode('utf-8')) def download_opds(self): opdsCatalogUrl = self.currentOpdsCatalogs.get( self.opdsCatalogSelector.currentText(), None) if opdsCatalogUrl is None: # Just give up quietly return self.model.downloadOpdsCatalog(self.gui, opdsCatalogUrl) if self.model.isCalibreOpdsServer(): self.model.downloadMetadataUsingCalibreRestApi( self.opdsUrlEditor.currentText()) self.library_view.horizontalHeader().setSectionResizeMode( 0, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode( 1, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode( 2, QHeaderView.Stretch) self.resizeAllLibraryViewLinesToHeaderHeight() self.resize(self.sizeHint()) def config(self): self.do_user_config(parent=self) def downloadSelectedBooks(self): selectionmodel = self.library_view.selectionModel() if selectionmodel.hasSelection(): rows = selectionmodel.selectedRows() for row in reversed(rows): book = row.data(Qt.UserRole) self.downloadBook(book) def downloadBook(self, book): if len(book.links) > 0: self.gui.download_ebook(book.links[0]) def fixBookTimestamps(self): selectionmodel = self.library_view.selectionModel() if selectionmodel.hasSelection(): rows = selectionmodel.selectedRows() for row in reversed(rows): book = row.data(Qt.UserRole) self.fixBookTimestamp(book) def fixBookTimestamp(self, book): bookTimestamp = book.timestamp identicalBookIds = self.findIdenticalBooksForBooksWithMultipleAuthors( book) bookIdToValMap = {} for identicalBookId in identicalBookIds: bookIdToValMap[identicalBookId] = bookTimestamp if len(bookIdToValMap) < 1: print "Failed to set timestamp of book: %s" % book self.db.set_field('timestamp', bookIdToValMap) def findIdenticalBooksForBooksWithMultipleAuthors(self, book): authorsList = book.authors if len(authorsList) < 2: return self.db.find_identical_books(book) # Try matching the authors one by one identicalBookIds = set() for author in authorsList: singleAuthorBook = Metadata(book.title, [author]) singleAuthorIdenticalBookIds = self.db.find_identical_books( singleAuthorBook) identicalBookIds = identicalBookIds.union( singleAuthorIdenticalBookIds) return identicalBookIds def dummy_books(self): dummy_author = ' ' * 40 dummy_title = ' ' * 60 books_list = [] for line in range(1, 10): book = DynamicBook() book.author = dummy_author book.title = dummy_title book.updated = datetime.datetime.now().strftime( '%Y-%m-%dT%H:%M:%S+00:00') book.id = '' books_list.append(book) return books_list def resizeAllLibraryViewLinesToHeaderHeight(self): rowHeight = self.library_view.horizontalHeader().height() for rowNumber in range(0, self.library_view.model().rowCount()): self.library_view.setRowHeight(rowNumber, rowHeight)
def __init__(self, data, key, text, parent): QCheckBox.__init__(self, text, parent) self.data, self.key = data, key self.setChecked(data.get(key, False)) self.stateChanged.connect(self._changed)
def __init__(self, gui, icon, do_user_config): QDialog.__init__(self, gui) self.gui = gui self.do_user_config = do_user_config self.db = gui.current_db.new_api # The model for the book list self.model = OpdsBooksModel(None, self.dummy_books(), self.db) self.searchproxymodel = QSortFilterProxyModel(self) self.searchproxymodel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.searchproxymodel.setFilterKeyColumn(-1) self.searchproxymodel.setSourceModel(self.model) self.layout = QGridLayout() self.setLayout(self.layout) self.setWindowTitle('OPDS Client') self.setWindowIcon(icon) labelColumnWidths = [] self.opdsUrlLabel = QLabel('OPDS URL: ') self.layout.addWidget(self.opdsUrlLabel, 0, 0) labelColumnWidths.append( self.layout.itemAtPosition(0, 0).sizeHint().width()) config.convertSingleStringOpdsUrlPreferenceToListOfStringsPreference() self.opdsUrlEditor = QComboBox(self) self.opdsUrlEditor.activated.connect(self.opdsUrlEditorActivated) self.opdsUrlEditor.addItems(prefs['opds_url']) self.opdsUrlEditor.setEditable(True) self.opdsUrlEditor.setInsertPolicy(QComboBox.InsertAtTop) self.layout.addWidget(self.opdsUrlEditor, 0, 1, 1, 3) self.opdsUrlLabel.setBuddy(self.opdsUrlEditor) buttonColumnNumber = 7 buttonColumnWidths = [] self.about_button = QPushButton('About', self) self.about_button.setAutoDefault(False) self.about_button.clicked.connect(self.about) self.layout.addWidget(self.about_button, 0, buttonColumnNumber) buttonColumnWidths.append( self.layout.itemAtPosition(0, buttonColumnNumber).sizeHint().width()) # Initially download the catalogs found in the root catalog of the URL # selected at startup. Fail quietly on failing to open the URL catalogsTuple = self.model.downloadOpdsRootCatalog( self.gui, self.opdsUrlEditor.currentText(), False) print catalogsTuple firstCatalogTitle = catalogsTuple[0] self.currentOpdsCatalogs = catalogsTuple[ 1] # A dictionary of title->feedURL self.opdsCatalogSelectorLabel = QLabel('OPDS Catalog:') self.layout.addWidget(self.opdsCatalogSelectorLabel, 1, 0) labelColumnWidths.append( self.layout.itemAtPosition(1, 0).sizeHint().width()) self.opdsCatalogSelector = QComboBox(self) self.opdsCatalogSelector.setEditable(False) self.opdsCatalogSelectorModel = QStringListModel( self.currentOpdsCatalogs.keys()) self.opdsCatalogSelector.setModel(self.opdsCatalogSelectorModel) self.opdsCatalogSelector.setCurrentText(firstCatalogTitle) self.layout.addWidget(self.opdsCatalogSelector, 1, 1, 1, 3) self.download_opds_button = QPushButton('Download OPDS', self) self.download_opds_button.setAutoDefault(False) self.download_opds_button.clicked.connect(self.download_opds) self.layout.addWidget(self.download_opds_button, 1, buttonColumnNumber) buttonColumnWidths.append( self.layout.itemAtPosition(1, buttonColumnNumber).sizeHint().width()) # Search GUI self.searchEditor = QLineEdit(self) self.searchEditor.returnPressed.connect(self.searchBookList) self.layout.addWidget(self.searchEditor, 2, buttonColumnNumber - 2, 1, 2) self.searchButton = QPushButton('Search', self) self.searchButton.setAutoDefault(False) self.searchButton.clicked.connect(self.searchBookList) self.layout.addWidget(self.searchButton, 2, buttonColumnNumber) buttonColumnWidths.append( self.layout.itemAtPosition(2, buttonColumnNumber).sizeHint().width()) # The main book list self.library_view = QTableView(self) self.library_view.setAlternatingRowColors(True) self.library_view.setModel(self.searchproxymodel) self.library_view.horizontalHeader().setSectionResizeMode( 0, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode( 1, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode( 2, QHeaderView.Stretch) self.library_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.resizeAllLibraryViewLinesToHeaderHeight() self.library_view.resizeColumnsToContents() self.layout.addWidget(self.library_view, 3, 0, 3, buttonColumnNumber + 1) self.hideNewsCheckbox = QCheckBox('Hide Newspapers', self) self.hideNewsCheckbox.clicked.connect(self.setHideNewspapers) self.hideNewsCheckbox.setChecked(prefs['hideNewspapers']) self.layout.addWidget(self.hideNewsCheckbox, 6, 0, 1, 3) self.hideBooksAlreadyInLibraryCheckbox = QCheckBox( 'Hide books already in library', self) self.hideBooksAlreadyInLibraryCheckbox.clicked.connect( self.setHideBooksAlreadyInLibrary) self.hideBooksAlreadyInLibraryCheckbox.setChecked( prefs['hideBooksAlreadyInLibrary']) self.layout.addWidget(self.hideBooksAlreadyInLibraryCheckbox, 7, 0, 1, 3) # Let the checkbox initial state control the filtering self.model.setFilterBooksThatAreNewspapers( self.hideNewsCheckbox.isChecked()) self.model.setFilterBooksThatAreAlreadyInLibrary( self.hideBooksAlreadyInLibraryCheckbox.isChecked()) self.downloadButton = QPushButton('Download selected books', self) self.downloadButton.setAutoDefault(False) self.downloadButton.clicked.connect(self.downloadSelectedBooks) self.layout.addWidget(self.downloadButton, 6, buttonColumnNumber) buttonColumnWidths.append( self.layout.itemAtPosition(6, buttonColumnNumber).sizeHint().width()) self.fixTimestampButton = QPushButton('Fix timestamps of selection', self) self.fixTimestampButton.setAutoDefault(False) self.fixTimestampButton.clicked.connect(self.fixBookTimestamps) self.layout.addWidget(self.fixTimestampButton, 7, buttonColumnNumber) buttonColumnWidths.append( self.layout.itemAtPosition(7, buttonColumnNumber).sizeHint().width()) # Make all columns of the grid layout the same width as the button column buttonColumnWidth = max(buttonColumnWidths) for columnNumber in range(0, buttonColumnNumber): self.layout.setColumnMinimumWidth(columnNumber, buttonColumnWidth) # Make sure the first column isn't wider than the labels it holds labelColumnWidth = max(labelColumnWidths) self.layout.setColumnMinimumWidth(0, labelColumnWidth) self.resize(self.sizeHint())
class RestoreImageDialog(QDialog): def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.model = None self.setModal(modal) self.setWindowTitle("Restore model into image") lo = QVBoxLayout(self) lo.setContentsMargins(10, 10, 10, 10) lo.setSpacing(5) # file selector self.wfile_in = FileSelector(self, label="Input FITS file:", dialog_label="Input FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.ExistingFile) lo.addWidget(self.wfile_in) self.wfile_out = FileSelector(self, label="Output FITS file:", dialog_label="Output FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.AnyFile) lo.addWidget(self.wfile_out) # beam size lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) lo1.addWidget(QLabel("Restoring beam FWHM, major axis:", self)) self.wbmaj = QLineEdit(self) lo1.addWidget(self.wbmaj) lo1.addWidget(QLabel("\" minor axis:", self)) self.wbmin = QLineEdit(self) lo1.addWidget(self.wbmin) lo1.addWidget(QLabel("\" P.A.:", self)) self.wbpa = QLineEdit(self) lo1.addWidget(self.wbpa) lo1.addWidget(QLabel("\u00B0", self)) for w in self.wbmaj, self.wbmin, self.wbpa: w.setValidator(QDoubleValidator(self)) lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.wfile_psf = FileSelector( self, label="Set restoring beam by fitting PSF image:", dialog_label="PSF FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.ExistingFile) lo1.addSpacing(32) lo1.addWidget(self.wfile_psf) # selection only self.wselonly = QCheckBox("restore selected model sources only", self) lo.addWidget(self.wselonly) # OK/cancel buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(0, 0, 0, 0) lo2.setContentsMargins(5, 5, 5, 5) self.wokbtn = QPushButton("OK", self) self.wokbtn.setMinimumWidth(128) self.wokbtn.clicked.connect(self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) cancelbtn.clicked.connect(self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) # signals self.wfile_in.filenameSelected.connect(self._fileSelected) self.wfile_in.filenameSelected.connect(self._inputFileSelected) self.wfile_out.filenameSelected.connect(self._fileSelected) self.wfile_psf.filenameSelected.connect(self._psfFileSelected) # internal state self.qerrmsg = QErrorMessage(self) def setModel(self, model): nsel = len([src for src in model.sources if src.selected]) self.wselonly.setVisible(nsel > 0 and nsel < len(model.sources)) self.model = model self._fileSelected(None) def _fileSelected(self, filename): self.wokbtn.setEnabled( bool(self.wfile_in.filename() and self.wfile_out.filename())) def _inputFileSelected(self, filename): if filename: try: header = pyfits.open(filename)[0].header except Exception as err: self.qerrmsg.showMessage("Error reading FITS file %s: %s" % (filename, str(err))) self.wfile_in.setFilename("") return # try to get beam extents gx, gy, grot = [ header.get(x, None) for x in ('BMAJ', 'BMIN', 'BPA') ] if all([x is not None for x in (gx, gy, grot)]): # if beam size is already set, ask before overwriting print([ str(x.text()) for x in (self.wbmaj, self.wbmin, self.wbpa) ]) if any([bool(str(x.text())) for x in (self.wbmaj, self.wbmin, self.wbpa)]) and \ QMessageBox.question(self, "Set restoring beam", "Also reset restoring beam size from this FITS file?", QMessageBox.Yes | QMessageBox.No) != QMessageBox.Yes: return self.wbmaj.setText("%.2f" % (gx * 3600)) self.wbmin.setText("%.2f" % (gy * 3600)) self.wbpa.setText("%.2f" % grot) def _psfFileSelected(self, filename): busy = BusyIndicator() filename = str(filename) self.parent().showMessage("Fitting gaussian to PSF file %s" % filename) try: bmaj, bmin, pa = [x / DEG for x in Imaging.fitPsf(filename)] except Exception as err: busy.reset_cursor() self.qerrmsg.showMessage("Error fitting PSF file %s: %s" % (filename, str(err))) return bmaj *= 3600 * Imaging.FWHM bmin *= 3600 * Imaging.FWHM self.wbmaj.setText(str(bmaj)) self.wbmin.setText(str(bmin)) self.wbpa.setText(str(pa)) busy.reset_cursor() def accept(self): """Tries to restore the image, and closes the dialog if successful.""" # get list of sources to restore sources = self.model.sources sel_sources = [src for src in sources if src.selected] if len(sel_sources) > 0 and len(sel_sources) < len( sources) and self.wselonly.isChecked(): sources = sel_sources if not sources: self.qerrmsg.showMessage("No sources to restore.") return busy = BusyIndicator() # get filenames infile = self.wfile_in.filename() outfile = self.wfile_out.filename() self.parent().showMessage( "Restoring %d model sources to image %s, writing to %s" % (len(sources), infile, outfile)) # read fits file try: input_hdu = pyfits.open(infile)[0] except Exception as err: busy.reset_cursor() self.qerrmsg.showMessage("Error reading FITS file %s: %s" % (infile, str(err))) return # get beam sizes try: bmaj = float(str(self.wbmaj.text())) bmin = float(str(self.wbmin.text())) pa = float(str(self.wbpa.text()) or "0") except Exception as err: busy.reset_cursor() self.qerrmsg.showMessage("Invalid beam size specified") return bmaj = bmaj / (Imaging.FWHM * 3600) * DEG bmin = bmin / (Imaging.FWHM * 3600) * DEG pa = pa * DEG # restore try: Imaging.restoreSources(input_hdu, sources, bmaj, bmin, pa) except Exception as err: busy.reset_cursor() self.qerrmsg.showMessage("Error restoring model into image: %s" % str(err)) return # save fits file try: input_hdu.writeto(outfile, overwrite=True) except Exception as err: busy.reset_cursor() self.qerrmsg.showMessage("Error writing FITS file %s: %s" % (outfile, str(err))) return self.parent().loadImage(outfile) busy.reset_cursor() return QDialog.accept(self)
def __init__(self, recipe_model, parent=None): QDialog.__init__(self, parent) self.commit_on_change = True self.previous_urn = None self.setWindowIcon(QIcon(I('scheduler.png'))) self.l = l = QGridLayout(self) # Left panel self.h = h = QHBoxLayout() l.addLayout(h, 0, 0, 1, 1) self.search = s = SearchBox2(self) self.search.initialize('scheduler_search_history') self.search.setMinimumContentsLength(15) self.go_button = b = QToolButton(self) b.setText(_("Go")) b.clicked.connect(self.search.do_search) h.addWidget(s), h.addWidget(b) self.recipes = RecipesView(self) l.addWidget(self.recipes, 1, 0, 2, 1) self.recipe_model = recipe_model self.recipe_model.do_refresh() self.recipes.setModel(self.recipe_model) self.recipes.setFocus(Qt.FocusReason.OtherFocusReason) self.setWindowTitle( _("Schedule news download [{} sources]").format( self.recipe_model.showing_count)) self.search.search.connect(self.recipe_model.search) self.recipe_model.searched.connect( self.search.search_done, type=Qt.ConnectionType.QueuedConnection) self.recipe_model.searched.connect(self.search_done) # Right Panel self.scroll_area_contents = sac = QWidget(self) self.l.addWidget(sac, 0, 1, 2, 1) sac.v = v = QVBoxLayout(sac) v.setContentsMargins(0, 0, 0, 0) self.detail_box = QTabWidget(self) self.detail_box.setVisible(False) self.detail_box.setCurrentIndex(0) v.addWidget(self.detail_box) v.addItem( QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)) # First Tab (scheduling) self.tab = QWidget() self.detail_box.addTab(self.tab, _("&Schedule")) self.tab.v = vt = QVBoxLayout(self.tab) vt.setContentsMargins(0, 0, 0, 0) self.blurb = la = QLabel('blurb') la.setWordWrap(True), la.setOpenExternalLinks(True) vt.addWidget(la) self.frame = f = QFrame(self.tab) vt.addWidget(f) f.setFrameShape(f.StyledPanel) f.setFrameShadow(f.Raised) f.v = vf = QVBoxLayout(f) self.schedule = s = QCheckBox(_("&Schedule for download:"), f) self.schedule.stateChanged[int].connect(self.toggle_schedule_info) vf.addWidget(s) f.h = h = QHBoxLayout() vf.addLayout(h) self.days_of_week = QRadioButton(_("&Days of week"), f) self.days_of_month = QRadioButton(_("Da&ys of month"), f) self.every_x_days = QRadioButton(_("Every &x days"), f) self.days_of_week.setChecked(True) h.addWidget(self.days_of_week), h.addWidget( self.days_of_month), h.addWidget(self.every_x_days) self.schedule_stack = ss = QStackedWidget(f) self.schedule_widgets = [] for key in reversed(self.SCHEDULE_TYPES): self.schedule_widgets.insert(0, self.SCHEDULE_TYPES[key](self)) self.schedule_stack.insertWidget(0, self.schedule_widgets[0]) vf.addWidget(ss) self.last_downloaded = la = QLabel(f) la.setWordWrap(True) vf.addWidget(la) self.account = acc = QGroupBox(self.tab) acc.setTitle(_("&Account")) vt.addWidget(acc) acc.g = g = QGridLayout(acc) acc.unla = la = QLabel(_("&Username:"******"&Password:"******"&Show password"), self.account) spw.stateChanged[int].connect(self.set_pw_echo_mode) g.addWidget(spw, 2, 0, 1, 2) self.rla = la = QLabel( _("For the scheduling to work, you must leave calibre running.")) vt.addWidget(la) for b, c in iteritems(self.SCHEDULE_TYPES): b = getattr(self, b) b.toggled.connect(self.schedule_type_selected) b.setToolTip(textwrap.dedent(c.HELP)) # Second tab (advanced settings) self.tab2 = t2 = QWidget() self.detail_box.addTab(self.tab2, _("&Advanced")) self.tab2.g = g = QGridLayout(t2) g.setContentsMargins(0, 0, 0, 0) self.add_title_tag = tt = QCheckBox(_("Add &title as tag"), t2) g.addWidget(tt, 0, 0, 1, 2) t2.la = la = QLabel(_("&Extra tags:")) self.custom_tags = ct = QLineEdit(self) la.setBuddy(ct) g.addWidget(la), g.addWidget(ct, 1, 1) t2.la2 = la = QLabel(_("&Keep at most:")) la.setToolTip( _("Maximum number of copies (issues) of this recipe to keep. Set to 0 to keep all (disable)." )) self.keep_issues = ki = QSpinBox(t2) tt.toggled['bool'].connect(self.keep_issues.setEnabled) ki.setMaximum(100000), la.setBuddy(ki) ki.setToolTip( _("<p>When set, this option will cause calibre to keep, at most, the specified number of issues" " of this periodical. Every time a new issue is downloaded, the oldest one is deleted, if the" " total is larger than this number.\n<p>Note that this feature only works if you have the" " option to add the title as tag checked, above.\n<p>Also, the setting for deleting periodicals" " older than a number of days, below, takes priority over this setting." )) ki.setSpecialValueText(_("all issues")), ki.setSuffix(_(" issues")) g.addWidget(la), g.addWidget(ki, 2, 1) si = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding) g.addItem(si, 3, 1, 1, 1) # Bottom area self.hb = h = QHBoxLayout() self.l.addLayout(h, 2, 1, 1, 1) self.labt = la = QLabel(_("Delete downloaded &news older than:")) self.old_news = on = QSpinBox(self) on.setToolTip( _("<p>Delete downloaded news older than the specified number of days. Set to zero to disable.\n" "<p>You can also control the maximum number of issues of a specific periodical that are kept" " by clicking the Advanced tab for that periodical above.")) on.setSpecialValueText(_("never delete")), on.setSuffix(_(" days")) on.setMaximum(1000), la.setBuddy(on) on.setValue(gconf['oldest_news']) h.addWidget(la), h.addWidget(on) self.download_all_button = b = QPushButton( QIcon(I('news.png')), _("Download &all scheduled"), self) b.setToolTip(_("Download all scheduled news sources at once")) b.clicked.connect(self.download_all_clicked) self.l.addWidget(b, 3, 0, 1, 1) self.bb = bb = QDialogButtonBox( QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel, self) bb.accepted.connect(self.accept), bb.rejected.connect(self.reject) self.download_button = b = bb.addButton(_('&Download now'), bb.ActionRole) b.setIcon(QIcon(I('arrow-down.png'))), b.setVisible(False) b.clicked.connect(self.download_clicked) self.l.addWidget(bb, 3, 1, 1, 1) geom = gprefs.get('scheduler_dialog_geometry') if geom is not None: QApplication.instance().safe_restore_geometry(self, geom)
class MainWindowMPDJ(QMainWindow): """Display the main window of the application where the connections and other stuff is edited by the user.""" def show(self): """Shows this window (maximized). I will work on a better solution.""" QMainWindow.showMaximized(self) def update(self): """Updates the view.""" global_properties = GlobalProperties.get_instance() mpdj_data = global_properties.mpdj_data self.tf_min_per_selection.setText( str(mpdj_data.min_units_per_node_touch)) self.tf_max_per_selection.setText( str(mpdj_data.max_units_per_node_touch)) self.tf_min_global_song_duration.setText( str(mpdj_data.global_min_song_duration)) self.tf_max_global_song_duration.setText( str(mpdj_data.global_max_song_duration)) self.cb_global_limit_overflow.setChecked( mpdj_data.limit_overspill_global) self.tf_global_node_max_overflow_minutes.setText( str(mpdj_data.global_node_max_overspill)) self.tf_global_node_max_overflow_minutes.setDisabled( not mpdj_data.limit_overspill_global) text_to_find = mpdj_data.unit_per_node_touch.gui_representation() index = self.combo_box_minutes_or_titles.findText( text_to_find, Qt.MatchFixedString) self.combo_box_minutes_or_titles.setCurrentIndex(index) # self.limit_artist_play_chk_box.setChecked( # global_properties.mpdj_data.limit_artist_in_node_touch) self.chk_box_graph_is_directed.setChecked(mpdj_data.graph_is_directed) self.setWindowTitle('MPDJ: {}'.format( global_properties.path_of_current_file)) def write_min_global_song_duration_to_mpdj(self): """Writes min global song duration to mpdj.""" global_properties = GlobalProperties.get_instance() global_properties.mpdj_data.global_min_song_duration = int( self.tf_min_global_song_duration.text()) def write_max_global_song_duration_to_mpdj(self): """Writes min global song duration to mpdj.""" global_properties = GlobalProperties.get_instance() global_properties.mpdj_data.global_max_song_duration = int( self.tf_max_global_song_duration.text()) def write_min_per_note_to_mpdj(self): """Write the selected min count per node touch to the mpdj which is currently worked on.""" global_properties = GlobalProperties.get_instance() global_properties.mpdj_data.min_units_per_node_touch = int( self.tf_min_per_selection.text()) def write_max_per_note_to_mpdj(self): """Writes the selected max count per node touch to the mpdj which is currently worked on.""" global_properties = GlobalProperties.get_instance() global_properties.mpdj_data.max_units_per_node_touch = int( self.tf_max_per_selection.text()) def write_unit_per_node_touch_to_mpdj(self): """Writes the selected unit for min and max per touch to the mpdj. This does not do anything at the moment. Has to be implemented properly""" global_properties = GlobalProperties.get_instance() selection_text = self.combo_box_minutes_or_titles.currentText() global_properties.mpdj_data.unit_per_node_touch = UnitPerNodeTouch[ selection_text.upper()] def write_limit_artists_played_to_mpdj(self): # """Write to the current mpdj if the artist are limited per node touch.""" global_properties = GlobalProperties.get_instance() state = self.limit_artist_play_chk_box.isChecked() global_properties.mpdj_data.limit_artists_in_node_touch = state def write_limit_overspill_to_mpdj(self): global_properties = GlobalProperties.get_instance() state = self.cb_global_limit_overflow.isChecked() global_properties.mpdj_data.limit_overspill_global = state def write_global_node_max_overflow_to_mpdj(self): global_properties = GlobalProperties.get_instance() global_node_max_overflow_text = self.tf_global_node_max_overflow_minutes.text( ) if global_node_max_overflow_text: new_max_overflow = int(global_node_max_overflow_text) global_properties.mpdj_data.global_node_max_overspill = new_max_overflow def write_graph_is_directed_to_mpdj(self): """Write if the graph is directed to mpd, this should be changed somehow, since this does only concern the editing of the connections.""" global_properties = GlobalProperties.get_instance() state = self.chk_box_graph_is_directed.isChecked() global_properties.mpdj_data.graph_is_directed = state def file_dialog(self, load_save_type=QFileDialog.AcceptSave): """Opens an file save dialog and returns the selected filename.""" file_save_dialog = QFileDialog(self) file_save_dialog.setFileMode(QFileDialog.AnyFile) file_save_dialog.setAcceptMode(load_save_type) file_save_dialog.setNameFilters( ["MPDJ files (*.{})".format(FILE_SUFFIX)]) file_save_dialog.selectNameFilter( "MPDJ files (*.{})".format(FILE_SUFFIX)) file_save_dialog.setDefaultSuffix((FILE_SUFFIX)) exec_value = file_save_dialog.exec() if exec_value == 0: return None file_names = file_save_dialog.selectedFiles() if len(file_names) != 1: message_box = QMessageBox() message_box.setText('Please select only one file!') message_box.setWindowTitle('Save error.') message_box.setStandardButtons(QMessageBox.Ok) message_box.setIcon(QMessageBox.Information) message_box.exec_() return None return file_names[0] def file_save_as(self): """Saves the file. opens a file_dialog which asks for the filename.""" file_name = self.file_dialog(load_save_type=QFileDialog.AcceptSave) if file_name: self.save_mpdj_data_to_file(file_name) def save_mpdj_data_to_file(self, p_file_name: str): """Saves the current mpdj data to the file by the path given in p_file_name.""" try: global_properties = GlobalProperties.get_instance() global_properties.save_mpdj_data_to_file(p_file_name) self.statusBar().showMessage('Saved to {}'.format(p_file_name), 5000) except (OSError, IOError) as exception: message_box = QMessageBox() message_box.setText('Error saving the file: {}'.format( str(exception))) message_box.setWindowTitle('Error saving the file.') message_box.setStandardButtons(QMessageBox.Ok) message_box.setIcon(QMessageBox.Warning) message_box.exec_() def file_save(self): """Saves the current mpdj data to the current file.""" global_properties = GlobalProperties.get_instance() if len(global_properties.path_of_current_file) > 0: self.save_mpdj_data_to_file(global_properties.path_of_current_file) else: self.file_save_as() def file_load(self): """Loads mpdj data from a file. Opens a file dialog which asks for the file to load.""" global_properties = GlobalProperties.get_instance() file_name = self.file_dialog(load_save_type=QFileDialog.AcceptOpen) if file_name: if global_properties.changes_happened_since_last_save: retval = show_discard_data_ok_cancel_message() if not global_properties.changes_happened_since_last_save or retval == QMessageBox.Ok: try: global_properties.load_mpdjdata_from_file(file_name) except AttributeError as err: message_box = QMessageBox() message_box.setText( 'Error reading your MPDJ-File: {}'.format(err)) message_box.setWindowTitle('Load error.') message_box.setStandardButtons(QMessageBox.Ok) message_box.setIcon(QMessageBox.Warning) message_box.exec_() def __init__(self): """Constructor""" QMainWindow.__init__(self) global_properties = GlobalProperties.get_instance() self.connection_table = ConnectionTableWidget() global_properties.add_listener(self.connection_table) self.setCentralWidget(self.connection_table) self.connection_table.update() self.menu_bar = self.menuBar() self.menu_file = self.menu_bar.addMenu('File') self.menu_file.addAction('New', file_new_clicked) self.menu_file.addAction('Open', self.file_load) self.menu_file.addSeparator() self.menu_file.addAction('Save', self.file_save) self.menu_file.addAction('Save as', self.file_save_as) self.menu_file.addSeparator() self.menu_file.addAction('Exit', sys.exit) self.menu_file = self.menu_bar.addMenu('Connections') self.make_birectional_menu = self.menu_file.addMenu( 'Make bidirectional') self.make_birectional_menu.addAction("with and", make_bidirectional_and) self.make_birectional_menu.addAction("with or", make_bidirectional_or) self.menu_selection = self.menu_bar.addMenu('Selections') self.action_add_selection = self.menu_selection.addAction( 'Add Selection') self.action_add_selection.triggered.connect( create_add_selection_window) self.action_merge_selections = self.menu_selection.addAction( 'Merge Selections') self.action_merge_selections.triggered.connect( create_merge_selectoon_window) self.setMenuBar(self.menu_bar) self.statusBar().showMessage('Welcome to mpdj!', 5000) self.mpdj_options_dock = QDockWidget("MPDJ Options Panel", self) self.mpdj_options_dock_layout = QFormLayout() self.mpdj_docked_widget = QWidget() self.tf_min_global_song_duration = QLineEdit() self.tf_min_global_song_duration.setValidator( QIntValidator(0, 2147483647)) self.mpdj_options_dock_layout.addRow('Global min song duration:', self.tf_min_global_song_duration) self.tf_min_global_song_duration.editingFinished.connect( self.write_min_global_song_duration_to_mpdj) self.tf_max_global_song_duration = QLineEdit() self.tf_max_global_song_duration.setValidator( QIntValidator(0, 2147483647)) self.mpdj_options_dock_layout.addRow('Global max song duration:', self.tf_max_global_song_duration) self.tf_max_global_song_duration.editingFinished.connect( self.write_max_global_song_duration_to_mpdj) self.tf_min_per_selection = QLineEdit() self.tf_min_per_selection.setValidator(QIntValidator(0, 2147483647)) self.mpdj_options_dock_layout.addRow('Min per Node touch:', self.tf_min_per_selection) self.tf_min_per_selection.editingFinished.connect( self.write_min_per_note_to_mpdj) self.tf_max_per_selection = QLineEdit() self.tf_max_per_selection.setValidator(QIntValidator(0, 2147483647)) self.mpdj_options_dock_layout.addRow('Max per Node touch:', self.tf_max_per_selection) self.tf_max_per_selection.editingFinished.connect( self.write_max_per_note_to_mpdj) self.combo_box_minutes_or_titles = QComboBox() self.combo_box_minutes_or_titles.addItems( [unit.gui_representation() for unit in UnitPerNodeTouch]) self.mpdj_options_dock_layout.addRow('Unit:', self.combo_box_minutes_or_titles) self.combo_box_minutes_or_titles.currentTextChanged.connect( self.write_unit_per_node_touch_to_mpdj) self.tf_global_node_max_overflow_minutes = QLineEdit() self.tf_global_node_max_overflow_minutes.setValidator( QIntValidator(0, 2147483647)) self.tf_global_node_max_overflow_minutes.editingFinished.connect( self.write_global_node_max_overflow_to_mpdj) self.cb_global_limit_overflow = QCheckBox() self.cb_global_limit_overflow.stateChanged.connect(lambda: list( map(lambda m: m(), [ lambda: self.tf_global_node_max_overflow_minutes.setDisabled( not self.cb_global_limit_overflow.isChecked()), self. write_limit_overspill_to_mpdj ]))) self.overflow_layoout = QHBoxLayout() self.overflow_layoout.addWidget(self.cb_global_limit_overflow) self.overflow_layoout.addWidget( self.tf_global_node_max_overflow_minutes) self.mpdj_options_dock_layout.addRow("Limit overflow:", self.overflow_layoout) self.mpdj_docked_widget.setLayout(self.mpdj_options_dock_layout) self.mpdj_options_dock.setWidget(self.mpdj_docked_widget) self.addDockWidget(Qt.LeftDockWidgetArea, self.mpdj_options_dock) self.chk_box_graph_is_directed = QCheckBox() self.chk_box_graph_is_directed.stateChanged.connect( self.write_graph_is_directed_to_mpdj) self.mpdj_options_dock_layout.addRow(QLabel('Graph is directed'), self.chk_box_graph_is_directed) self.opened_selection_window = None global_properties.add_listener(self) self.update()
class BulkSeries(BulkBase): def setup_ui(self, parent): self.make_widgets(parent, EditWithComplete) values = self.all_values = list(self.db.all_custom(num=self.col_id)) values.sort(key=sort_key) self.main_widget.setSizeAdjustPolicy(self.main_widget.AdjustToMinimumContentsLengthWithIcon) self.main_widget.setMinimumContentsLength(25) self.widgets.append(QLabel('', parent)) w = QWidget(parent) layout = QHBoxLayout(w) layout.setContentsMargins(0, 0, 0, 0) self.remove_series = QCheckBox(parent) self.remove_series.setText(_('Clear series')) layout.addWidget(self.remove_series) self.idx_widget = QCheckBox(parent) self.idx_widget.setText(_('Automatically number books')) self.idx_widget.setToolTip('<p>' + _( 'If not checked, the series number for the books will be set to 1. ' 'If checked, selected books will be automatically numbered, ' 'in the order you selected them. So if you selected ' 'Book A and then Book B, Book A will have series number 1 ' 'and Book B series number 2.') + '</p>') layout.addWidget(self.idx_widget) self.force_number = QCheckBox(parent) self.force_number.setText(_('Force numbers to start with ')) self.force_number.setToolTip('<p>' + _( 'Series will normally be renumbered from the highest ' 'number in the database for that series. Checking this ' 'box will tell calibre to start numbering from the value ' 'in the box') + '</p>') layout.addWidget(self.force_number) self.series_start_number = QDoubleSpinBox(parent) self.series_start_number.setMinimum(0.0) self.series_start_number.setMaximum(9999999.0) self.series_start_number.setProperty("value", 1.0) layout.addWidget(self.series_start_number) self.series_increment = QDoubleSpinBox(parent) self.series_increment.setMinimum(0.00) self.series_increment.setMaximum(99999.0) self.series_increment.setProperty("value", 1.0) self.series_increment.setToolTip('<p>' + _( 'The amount by which to increment the series number ' 'for successive books. Only applicable when using ' 'force series numbers.') + '</p>') self.series_increment.setPrefix('+') layout.addWidget(self.series_increment) layout.addItem(QSpacerItem(20, 10, QSizePolicy.Expanding, QSizePolicy.Minimum)) self.widgets.append(w) self.idx_widget.stateChanged.connect(self.a_c_checkbox_changed) self.force_number.stateChanged.connect(self.a_c_checkbox_changed) self.series_start_number.valueChanged.connect(self.a_c_checkbox_changed) self.series_increment.valueChanged.connect(self.a_c_checkbox_changed) self.remove_series.stateChanged.connect(self.a_c_checkbox_changed) self.main_widget self.ignore_change_signals = False def a_c_checkbox_changed(self): def disable_numbering_checkboxes(idx_widget_enable): if idx_widget_enable: self.idx_widget.setEnabled(True) else: self.idx_widget.setChecked(False) self.idx_widget.setEnabled(False) self.force_number.setChecked(False) self.force_number.setEnabled(False) self.series_start_number.setEnabled(False) self.series_increment.setEnabled(False) if self.ignore_change_signals: return self.ignore_change_signals = True apply_changes = False if self.remove_series.isChecked(): self.main_widget.setText('') self.main_widget.setEnabled(False) disable_numbering_checkboxes(idx_widget_enable=False) apply_changes = True elif self.main_widget.text(): self.remove_series.setEnabled(False) self.idx_widget.setEnabled(True) apply_changes = True else: # no text, no clear. Basically reinitialize self.main_widget.setEnabled(True) self.remove_series.setEnabled(True) disable_numbering_checkboxes(idx_widget_enable=False) apply_changes = False self.force_number.setEnabled(self.idx_widget.isChecked()) self.series_start_number.setEnabled(self.force_number.isChecked()) self.series_increment.setEnabled(self.force_number.isChecked()) self.ignore_change_signals = False self.a_c_checkbox.setChecked(apply_changes) def initialize(self, book_id): self.idx_widget.setChecked(False) self.main_widget.set_separator(None) self.main_widget.update_items_cache(self.all_values) self.main_widget.setEditText('') self.a_c_checkbox.setChecked(False) def getter(self): n = unicode(self.main_widget.currentText()).strip() autonumber = self.idx_widget.checkState() force = self.force_number.checkState() start = self.series_start_number.value() remove = self.remove_series.checkState() increment = self.series_increment.value() return n, autonumber, force, start, remove, increment def commit(self, book_ids, notify=False): if not self.a_c_checkbox.isChecked(): return val, update_indices, force_start, at_value, clear, increment = self.gui_val val = None if clear else self.normalize_ui_val(val) if clear or val != '': extras = [] for book_id in book_ids: if clear: extras.append(None) continue if update_indices: if force_start: s_index = at_value at_value += increment elif tweaks['series_index_auto_increment'] != 'const': s_index = self.db.get_next_cc_series_num_for(val, num=self.col_id) else: s_index = 1.0 else: s_index = self.db.get_custom_extra(book_id, num=self.col_id, index_is_id=True) extras.append(s_index) self.db.set_custom_bulk(book_ids, val, extras=extras, num=self.col_id, notify=notify)
def __init__(self): """Constructor""" QMainWindow.__init__(self) global_properties = GlobalProperties.get_instance() self.connection_table = ConnectionTableWidget() global_properties.add_listener(self.connection_table) self.setCentralWidget(self.connection_table) self.connection_table.update() self.menu_bar = self.menuBar() self.menu_file = self.menu_bar.addMenu('File') self.menu_file.addAction('New', file_new_clicked) self.menu_file.addAction('Open', self.file_load) self.menu_file.addSeparator() self.menu_file.addAction('Save', self.file_save) self.menu_file.addAction('Save as', self.file_save_as) self.menu_file.addSeparator() self.menu_file.addAction('Exit', sys.exit) self.menu_file = self.menu_bar.addMenu('Connections') self.make_birectional_menu = self.menu_file.addMenu( 'Make bidirectional') self.make_birectional_menu.addAction("with and", make_bidirectional_and) self.make_birectional_menu.addAction("with or", make_bidirectional_or) self.menu_selection = self.menu_bar.addMenu('Selections') self.action_add_selection = self.menu_selection.addAction( 'Add Selection') self.action_add_selection.triggered.connect( create_add_selection_window) self.action_merge_selections = self.menu_selection.addAction( 'Merge Selections') self.action_merge_selections.triggered.connect( create_merge_selectoon_window) self.setMenuBar(self.menu_bar) self.statusBar().showMessage('Welcome to mpdj!', 5000) self.mpdj_options_dock = QDockWidget("MPDJ Options Panel", self) self.mpdj_options_dock_layout = QFormLayout() self.mpdj_docked_widget = QWidget() self.tf_min_global_song_duration = QLineEdit() self.tf_min_global_song_duration.setValidator( QIntValidator(0, 2147483647)) self.mpdj_options_dock_layout.addRow('Global min song duration:', self.tf_min_global_song_duration) self.tf_min_global_song_duration.editingFinished.connect( self.write_min_global_song_duration_to_mpdj) self.tf_max_global_song_duration = QLineEdit() self.tf_max_global_song_duration.setValidator( QIntValidator(0, 2147483647)) self.mpdj_options_dock_layout.addRow('Global max song duration:', self.tf_max_global_song_duration) self.tf_max_global_song_duration.editingFinished.connect( self.write_max_global_song_duration_to_mpdj) self.tf_min_per_selection = QLineEdit() self.tf_min_per_selection.setValidator(QIntValidator(0, 2147483647)) self.mpdj_options_dock_layout.addRow('Min per Node touch:', self.tf_min_per_selection) self.tf_min_per_selection.editingFinished.connect( self.write_min_per_note_to_mpdj) self.tf_max_per_selection = QLineEdit() self.tf_max_per_selection.setValidator(QIntValidator(0, 2147483647)) self.mpdj_options_dock_layout.addRow('Max per Node touch:', self.tf_max_per_selection) self.tf_max_per_selection.editingFinished.connect( self.write_max_per_note_to_mpdj) self.combo_box_minutes_or_titles = QComboBox() self.combo_box_minutes_or_titles.addItems( [unit.gui_representation() for unit in UnitPerNodeTouch]) self.mpdj_options_dock_layout.addRow('Unit:', self.combo_box_minutes_or_titles) self.combo_box_minutes_or_titles.currentTextChanged.connect( self.write_unit_per_node_touch_to_mpdj) self.tf_global_node_max_overflow_minutes = QLineEdit() self.tf_global_node_max_overflow_minutes.setValidator( QIntValidator(0, 2147483647)) self.tf_global_node_max_overflow_minutes.editingFinished.connect( self.write_global_node_max_overflow_to_mpdj) self.cb_global_limit_overflow = QCheckBox() self.cb_global_limit_overflow.stateChanged.connect(lambda: list( map(lambda m: m(), [ lambda: self.tf_global_node_max_overflow_minutes.setDisabled( not self.cb_global_limit_overflow.isChecked()), self. write_limit_overspill_to_mpdj ]))) self.overflow_layoout = QHBoxLayout() self.overflow_layoout.addWidget(self.cb_global_limit_overflow) self.overflow_layoout.addWidget( self.tf_global_node_max_overflow_minutes) self.mpdj_options_dock_layout.addRow("Limit overflow:", self.overflow_layoout) self.mpdj_docked_widget.setLayout(self.mpdj_options_dock_layout) self.mpdj_options_dock.setWidget(self.mpdj_docked_widget) self.addDockWidget(Qt.LeftDockWidgetArea, self.mpdj_options_dock) self.chk_box_graph_is_directed = QCheckBox() self.chk_box_graph_is_directed.stateChanged.connect( self.write_graph_is_directed_to_mpdj) self.mpdj_options_dock_layout.addRow(QLabel('Graph is directed'), self.chk_box_graph_is_directed) self.opened_selection_window = None global_properties.add_listener(self) self.update()
class BulkBase(Base): @property def gui_val(self): if not hasattr(self, '_cached_gui_val_'): self._cached_gui_val_ = self.getter() return self._cached_gui_val_ def get_initial_value(self, book_ids): values = set([]) for book_id in book_ids: val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True) if isinstance(val, list): val = frozenset(val) values.add(val) if len(values) > 1: break ans = None if len(values) == 1: ans = iter(values).next() if isinstance(ans, frozenset): ans = list(ans) return ans def initialize(self, book_ids): self.initial_val = val = self.get_initial_value(book_ids) val = self.normalize_db_val(val) self.setter(val) def commit(self, book_ids, notify=False): if not self.a_c_checkbox.isChecked(): return val = self.gui_val val = self.normalize_ui_val(val) self.db.set_custom_bulk(book_ids, val, num=self.col_id, notify=notify) def make_widgets(self, parent, main_widget_class, extra_label_text=''): w = QWidget(parent) self.widgets = [QLabel('&'+self.col_metadata['name']+':', w), w] l = QHBoxLayout() l.setContentsMargins(0, 0, 0, 0) w.setLayout(l) self.main_widget = main_widget_class(w) l.addWidget(self.main_widget) l.setStretchFactor(self.main_widget, 10) self.a_c_checkbox = QCheckBox(_('Apply changes'), w) l.addWidget(self.a_c_checkbox) self.ignore_change_signals = True # connect to the various changed signals so we can auto-update the # apply changes checkbox if hasattr(self.main_widget, 'editTextChanged'): # editable combobox widgets self.main_widget.editTextChanged.connect(self.a_c_checkbox_changed) if hasattr(self.main_widget, 'textChanged'): # lineEdit widgets self.main_widget.textChanged.connect(self.a_c_checkbox_changed) if hasattr(self.main_widget, 'currentIndexChanged'): # combobox widgets self.main_widget.currentIndexChanged[int].connect(self.a_c_checkbox_changed) if hasattr(self.main_widget, 'valueChanged'): # spinbox widgets self.main_widget.valueChanged.connect(self.a_c_checkbox_changed) if hasattr(self.main_widget, 'dateTimeChanged'): # dateEdit widgets self.main_widget.dateTimeChanged.connect(self.a_c_checkbox_changed) def a_c_checkbox_changed(self): if not self.ignore_change_signals: self.a_c_checkbox.setChecked(True)
class Report(QDialog): # {{{ def __init__(self, parent): QDialog.__init__(self, parent) self.gui = parent self.setAttribute(Qt.WA_DeleteOnClose, False) self.setWindowIcon(QIcon(I('polish.png'))) self.reports = [] self.l = l = QGridLayout() self.setLayout(l) self.view = v = QTextEdit(self) v.setReadOnly(True) l.addWidget(self.view, 0, 0, 1, 2) self.backup_msg = la = QLabel('') l.addWidget(la, 1, 0, 1, 2) la.setVisible(False) la.setWordWrap(True) self.ign = QCheckBox(_('Ignore remaining report'), self) l.addWidget(self.ign, 2, 0) bb = self.bb = QDialogButtonBox(QDialogButtonBox.Close) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) b = self.log_button = bb.addButton(_('View full &log'), bb.ActionRole) b.clicked.connect(self.view_log) bb.button(bb.Close).setDefault(True) l.addWidget(bb, 2, 1) self.finished.connect(self.show_next, type=Qt.QueuedConnection) self.resize(QSize(800, 600)) def setup_ign(self): self.ign.setText( ngettext('Ignore remaining report', 'Ignore remaining {} reports', len(self.reports)).format(len(self.reports))) self.ign.setVisible(bool(self.reports)) self.ign.setChecked(False) def __call__(self, *args): self.reports.append(args) self.setup_ign() if not self.isVisible(): self.show_next() def show_report(self, book_title, book_id, fmts, job, report): from calibre.ebooks.markdown import markdown self.current_log = job.details self.setWindowTitle(_('Polishing of %s') % book_title) self.view.setText( markdown('# %s\n\n' % book_title + report, output_format='html4')) self.bb.button(self.bb.Close).setFocus(Qt.OtherFocusReason) self.backup_msg.setVisible(bool(fmts)) if fmts: m = ngettext('The original file has been saved as %s.', 'The original files have been saved as %s.', len(fmts)) % (_(' and ').join('ORIGINAL_' + f for f in fmts)) self.backup_msg.setText(m + ' ' + _( 'If you polish again, the polishing will run on the originals.' ) % ()) def view_log(self): self.view.setPlainText(self.current_log) self.view.verticalScrollBar().setValue(0) def show_next(self, *args): if not self.reports: return if not self.isVisible(): self.show() self.show_report(*self.reports.pop(0)) self.setup_ign() def accept(self): if self.ign.isChecked(): self.reports = [] if self.reports: self.show_next() return super(Report, self).accept() def reject(self): if self.ign.isChecked(): self.reports = [] if self.reports: self.show_next() return super(Report, self).reject()
def __init__(self, db, book_id_map, parent=None): from calibre.ebooks.oeb.polish.main import HELP QDialog.__init__(self, parent) self.db, self.book_id_map = weakref.ref(db), book_id_map self.setWindowIcon(QIcon(I('polish.png'))) title = _('Polish book') if len(book_id_map) > 1: title = _('Polish %d books') % len(book_id_map) self.setWindowTitle(title) self.help_text = { 'polish': _('<h3>About Polishing books</h3>%s') % HELP['about'].format( _('''<p>If you have both EPUB and ORIGINAL_EPUB in your book, then polishing will run on ORIGINAL_EPUB (the same for other ORIGINAL_* formats). So if you want Polishing to not run on the ORIGINAL_* format, delete the ORIGINAL_* format before running it.</p>''')), 'embed': _('<h3>Embed referenced fonts</h3>%s') % HELP['embed'], 'subset': _('<h3>Subsetting fonts</h3>%s') % HELP['subset'], 'smarten_punctuation': _('<h3>Smarten punctuation</h3>%s') % HELP['smarten_punctuation'], 'metadata': _('<h3>Updating metadata</h3>' '<p>This will update all metadata <i>except</i> the cover in the' ' e-book files to match the current metadata in the' ' calibre library.</p>' ' <p>Note that most e-book' ' formats are not capable of supporting all the' ' metadata in calibre.</p><p>There is a separate option to' ' update the cover.</p>'), 'do_cover': _('<h3>Update cover</h3><p>Update the covers in the e-book files to match the' ' current cover in the calibre library.</p>' '<p>If the e-book file does not have' ' an identifiable cover, a new cover is inserted.</p>'), 'jacket': _('<h3>Book jacket</h3>%s') % HELP['jacket'], 'remove_jacket': _('<h3>Remove book jacket</h3>%s') % HELP['remove_jacket'], 'remove_unused_css': _('<h3>Remove unused CSS rules</h3>%s') % HELP['remove_unused_css'], 'compress_images': _('<h3>Losslessly compress images</h3>%s') % HELP['compress_images'], 'upgrade_book': _('<h3>Upgrade book internals</h3>%s') % HELP['upgrade_book'], } self.l = l = QGridLayout() self.setLayout(l) self.la = la = QLabel('<b>' + _('Select actions to perform:')) l.addWidget(la, 0, 0, 1, 2) count = 0 self.all_actions = OrderedDict([ ('embed', _('&Embed all referenced fonts')), ('subset', _('&Subset all embedded fonts')), ('smarten_punctuation', _('Smarten &punctuation')), ('metadata', _('Update &metadata in the book files')), ('do_cover', _('Update the &cover in the book files')), ('jacket', _('Add/replace metadata as a "book &jacket" page')), ('remove_jacket', _('&Remove a previously inserted book jacket')), ('remove_unused_css', _('Remove &unused CSS rules from the book')), ('compress_images', _('Losslessly &compress images')), ('upgrade_book', _('&Upgrade book internals')), ]) prefs = gprefs.get('polishing_settings', {}) for name, text in iteritems(self.all_actions): count += 1 x = QCheckBox(text, self) x.setChecked(prefs.get(name, False)) x.setObjectName(name) connect_lambda( x.stateChanged, self, lambda self, state: self.option_toggled( self.sender().objectName(), state)) l.addWidget(x, count, 0, 1, 1) setattr(self, 'opt_' + name, x) la = QLabel(' <a href="#%s">%s</a>' % (name, _('About'))) setattr(self, 'label_' + name, x) la.linkActivated.connect(self.help_link_activated) l.addWidget(la, count, 1, 1, 1) count += 1 l.addItem(QSpacerItem(10, 10, vPolicy=QSizePolicy.Expanding), count, 1, 1, 2) la = self.help_label = QLabel('') self.help_link_activated('#polish') la.setWordWrap(True) la.setTextFormat(Qt.RichText) la.setFrameShape(QFrame.StyledPanel) la.setAlignment(Qt.AlignLeft | Qt.AlignTop) la.setLineWidth(2) la.setStyleSheet('QLabel { margin-left: 75px }') l.addWidget(la, 0, 2, count + 1, 1) l.setColumnStretch(2, 1) self.show_reports = sr = QCheckBox(_('Show &report'), self) sr.setChecked(gprefs.get('polish_show_reports', True)) sr.setToolTip( textwrap.fill( _('Show a report of all the actions performed' ' after polishing is completed'))) l.addWidget(sr, count + 1, 0, 1, 1) self.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) self.save_button = sb = bb.addButton(_('&Save Settings'), bb.ActionRole) sb.clicked.connect(self.save_settings) self.load_button = lb = bb.addButton(_('&Load Settings'), bb.ActionRole) self.load_menu = QMenu(lb) lb.setMenu(self.load_menu) self.all_button = b = bb.addButton(_('Select &all'), bb.ActionRole) connect_lambda(b.clicked, self, lambda self: self.select_all(True)) self.none_button = b = bb.addButton(_('Select &none'), bb.ActionRole) connect_lambda(b.clicked, self, lambda self: self.select_all(False)) l.addWidget(bb, count + 1, 1, 1, -1) self.setup_load_button() self.resize(QSize(950, 600))
def __init__(self, process): super().__init__() self._process = process self._check_box = QCheckBox(process.get_name()) self._setup_ui()
class BulkBase(Base): @property def gui_val(self): if not hasattr(self, '_cached_gui_val_'): self._cached_gui_val_ = self.getter() return self._cached_gui_val_ def get_initial_value(self, book_ids): values = set([]) for book_id in book_ids: val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True) if isinstance(val, list): val = frozenset(val) values.add(val) if len(values) > 1: break ans = None if len(values) == 1: ans = iter(values).next() if isinstance(ans, frozenset): ans = list(ans) return ans def initialize(self, book_ids): self.initial_val = val = self.get_initial_value(book_ids) val = self.normalize_db_val(val) self.setter(val) def commit(self, book_ids, notify=False): if not self.a_c_checkbox.isChecked(): return val = self.gui_val val = self.normalize_ui_val(val) self.db.set_custom_bulk(book_ids, val, num=self.col_id, notify=notify) def make_widgets(self, parent, main_widget_class, extra_label_text=''): w = QWidget(parent) self.widgets = [QLabel('&' + self.col_metadata['name'] + ':', w), w] l = QHBoxLayout() l.setContentsMargins(0, 0, 0, 0) w.setLayout(l) self.main_widget = main_widget_class(w) l.addWidget(self.main_widget) l.setStretchFactor(self.main_widget, 10) self.a_c_checkbox = QCheckBox(_('Apply changes'), w) l.addWidget(self.a_c_checkbox) self.ignore_change_signals = True # connect to the various changed signals so we can auto-update the # apply changes checkbox if hasattr(self.main_widget, 'editTextChanged'): # editable combobox widgets self.main_widget.editTextChanged.connect(self.a_c_checkbox_changed) if hasattr(self.main_widget, 'textChanged'): # lineEdit widgets self.main_widget.textChanged.connect(self.a_c_checkbox_changed) if hasattr(self.main_widget, 'currentIndexChanged'): # combobox widgets self.main_widget.currentIndexChanged[int].connect( self.a_c_checkbox_changed) if hasattr(self.main_widget, 'valueChanged'): # spinbox widgets self.main_widget.valueChanged.connect(self.a_c_checkbox_changed) if hasattr(self.main_widget, 'dateTimeChanged'): # dateEdit widgets self.main_widget.dateTimeChanged.connect(self.a_c_checkbox_changed) def a_c_checkbox_changed(self): if not self.ignore_change_signals: self.a_c_checkbox.setChecked(True)
def __init__(self, parent, view, row, link_delegate): QDialog.__init__(self, parent) self.normal_brush = QBrush(Qt.white) self.marked_brush = QBrush(Qt.lightGray) self.marked = None self.gui = parent self.splitter = QSplitter(self) self._l = l = QVBoxLayout(self) self.setLayout(l) l.addWidget(self.splitter) self.cover = CoverView(self) self.cover.resizeEvent = self.cover_view_resized self.cover.cover_changed.connect(self.cover_changed) self.cover_pixmap = None self.cover.sizeHint = self.details_size_hint self.splitter.addWidget(self.cover) self.details = Details(parent.book_details.book_info, self) self.details.page().setLinkDelegationPolicy( self.details.page().DelegateAllLinks) self.details.linkClicked.connect(self.link_clicked) s = self.details.page().settings() s.setAttribute(s.JavascriptEnabled, False) self.css = css() self.link_delegate = link_delegate self.details.setAttribute(Qt.WA_OpaquePaintEvent, False) palette = self.details.palette() self.details.setAcceptDrops(False) palette.setBrush(QPalette.Base, Qt.transparent) self.details.page().setPalette(palette) self.c = QWidget(self) self.c.l = l2 = QGridLayout(self.c) self.c.setLayout(l2) l2.addWidget(self.details, 0, 0, 1, -1) self.splitter.addWidget(self.c) self.fit_cover = QCheckBox(_('Fit &cover within view'), self) self.fit_cover.setChecked( gprefs.get('book_info_dialog_fit_cover', True)) l2.addWidget(self.fit_cover, l2.rowCount(), 0, 1, -1) self.previous_button = QPushButton(QIcon(I('previous.png')), _('&Previous'), self) self.previous_button.clicked.connect(self.previous) l2.addWidget(self.previous_button, l2.rowCount(), 0) self.next_button = QPushButton(QIcon(I('next.png')), _('&Next'), self) self.next_button.clicked.connect(self.next) l2.addWidget(self.next_button, l2.rowCount() - 1, 1) self.view = view self.current_row = None self.refresh(row) self.view.model().new_bookdisplay_data.connect(self.slave) self.fit_cover.stateChanged.connect(self.toggle_cover_fit) self.ns = QShortcut(QKeySequence('Alt+Right'), self) self.ns.activated.connect(self.next) self.ps = QShortcut(QKeySequence('Alt+Left'), self) self.ps.activated.connect(self.previous) self.next_button.setToolTip( _('Next [%s]') % unicode(self.ns.key().toString(QKeySequence.NativeText))) self.previous_button.setToolTip( _('Previous [%s]') % unicode(self.ps.key().toString(QKeySequence.NativeText))) geom = QCoreApplication.instance().desktop().availableGeometry(self) screen_height = geom.height() - 100 screen_width = geom.width() - 100 self.resize(max(int(screen_width / 2), 700), screen_height) saved_layout = gprefs.get('book_info_dialog_layout', None) if saved_layout is not None: try: self.restoreGeometry(saved_layout[0]) self.splitter.restoreState(saved_layout[1]) except Exception: pass
class RuleEditor(QDialog): # {{{ @property def doing_multiple(self): return hasattr(self, 'multiple_icon_cb') and self.multiple_icon_cb.isChecked() def __init__(self, fm, pref_name, parent=None): QDialog.__init__(self, parent) self.fm = fm if pref_name == 'column_color_rules': self.rule_kind = 'color' rule_text = _('column coloring') elif pref_name == 'column_icon_rules': self.rule_kind = 'icon' rule_text = _('column icon') elif pref_name == 'cover_grid_icon_rules': self.rule_kind = 'emblem' rule_text = _('Cover grid emblem') self.setWindowIcon(QIcon(I('format-fill-color.png'))) self.setWindowTitle(_('Create/edit a {0} rule').format(rule_text)) self.l = l = QGridLayout(self) self.setLayout(l) self.l1 = l1 = QLabel(_('Create a {0} rule by' ' filling in the boxes below').format(rule_text)) l.addWidget(l1, 0, 0, 1, 8) self.f1 = QFrame(self) self.f1.setFrameShape(QFrame.HLine) l.addWidget(self.f1, 1, 0, 1, 8) self.l2 = l2 = QLabel(_('Add the emblem:') if self.rule_kind == 'emblem' else _('Set the')) l.addWidget(l2, 2, 0) if self.rule_kind == 'color': l.addWidget(QLabel(_('color'))) elif self.rule_kind == 'icon': self.kind_box = QComboBox(self) for tt, t in icon_rule_kinds: self.kind_box.addItem(tt, t) l.addWidget(self.kind_box, 2, 1) self.kind_box.setToolTip(textwrap.fill(_( 'If you choose composed icons and multiple rules match, then all the' ' matching icons will be combined, otherwise the icon from the' ' first rule to match will be used.'))) else: pass self.l3 = l3 = QLabel(_('of the column:')) l.addWidget(l3, 2, 2) self.column_box = QComboBox(self) l.addWidget(self.column_box, 2, 3) self.l4 = l4 = QLabel(_('to')) l.addWidget(l4, 2, 4) if self.rule_kind == 'emblem': l3.setVisible(False), self.column_box.setVisible(False), l4.setVisible(False) def create_filename_box(): self.filename_box = f = QComboBox() self.filenamebox_view = v = QListView() v.setIconSize(QSize(32, 32)) self.filename_box.setView(v) self.orig_filenamebox_view = f.view() f.setMinimumContentsLength(20), f.setSizeAdjustPolicy(f.AdjustToMinimumContentsLengthWithIcon) self.populate_icon_filenames() if self.rule_kind == 'color': self.color_box = ColorButton(parent=self) self.color_label = QLabel('Sample text Sample text') self.color_label.setTextFormat(Qt.RichText) l.addWidget(self.color_box, 2, 5) l.addWidget(self.color_label, 2, 6) l.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding), 2, 7) elif self.rule_kind == 'emblem': create_filename_box() self.update_filename_box() self.filename_button = QPushButton(QIcon(I('document_open.png')), _('&Add new image')) l.addWidget(self.filename_box) l.addWidget(self.filename_button, 2, 6) l.addWidget(QLabel(_('(Images should be square-ish)')), 2, 7) l.setColumnStretch(7, 10) else: create_filename_box() vb = QVBoxLayout() self.multiple_icon_cb = QCheckBox(_('Choose &more than one icon')) vb.addWidget(self.multiple_icon_cb) self.update_filename_box() self.multiple_icon_cb.clicked.connect(self.multiple_box_clicked) vb.addWidget(self.filename_box) l.addLayout(vb, 2, 5) self.filename_button = QPushButton(QIcon(I('document_open.png')), _('&Add icon')) l.addWidget(self.filename_button, 2, 6) l.addWidget(QLabel(_('Icons should be square or landscape')), 2, 7) l.setColumnStretch(7, 10) self.l5 = l5 = QLabel( _('Only if the following conditions are all satisfied:')) l.addWidget(l5, 3, 0, 1, 7) self.scroll_area = sa = QScrollArea(self) sa.setMinimumHeight(300) sa.setMinimumWidth(950) sa.setWidgetResizable(True) l.addWidget(sa, 4, 0, 1, 8) self.add_button = b = QPushButton(QIcon(I('plus.png')), _('Add &another condition')) l.addWidget(b, 5, 0, 1, 8) b.clicked.connect(self.add_blank_condition) self.l6 = l6 = QLabel(_('You can disable a condition by' ' blanking all of its boxes')) l.addWidget(l6, 6, 0, 1, 8) self.bb = bb = QDialogButtonBox( QDialogButtonBox.Ok|QDialogButtonBox.Cancel) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) l.addWidget(bb, 7, 0, 1, 8) if self.rule_kind != 'color': self.remove_button = b = bb.addButton(_('&Remove icon'), bb.ActionRole) b.setIcon(QIcon(I('minus.png'))) b.setMenu(QMenu()) b.setToolTip('<p>' + _('Remove a previously added icon. Note that doing so will cause rules that use it to stop working.')) self.update_remove_button() self.conditions_widget = QWidget(self) sa.setWidget(self.conditions_widget) self.conditions_widget.setLayout(QVBoxLayout()) self.conditions_widget.layout().setAlignment(Qt.AlignTop) self.conditions = [] if self.rule_kind == 'color': for b in (self.column_box, ): b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon) b.setMinimumContentsLength(15) for key in sorted(displayable_columns(fm), key=lambda k: sort_key(fm[k]['name']) if k != color_row_key else b''): if key == color_row_key and self.rule_kind != 'color': continue name = all_columns_string if key == color_row_key else fm[key]['name'] if name: self.column_box.addItem(name, key) self.column_box.setCurrentIndex(0) if self.rule_kind == 'color': self.color_box.color = '#000' self.update_color_label() self.color_box.color_changed.connect(self.update_color_label) else: self.rule_icon_files = [] self.filename_button.clicked.connect(self.filename_button_clicked) self.resize(self.sizeHint()) def multiple_box_clicked(self): self.update_filename_box() self.update_icon_filenames_in_box() @property def icon_folder(self): return os.path.join(config_dir, 'cc_icons') def populate_icon_filenames(self): d = self.icon_folder self.icon_file_names = [] if os.path.exists(d): for icon_file in os.listdir(d): icon_file = lower(icon_file) if os.path.exists(os.path.join(d, icon_file)) and icon_file.endswith('.png'): self.icon_file_names.append(icon_file) self.icon_file_names.sort(key=sort_key) def update_filename_box(self): doing_multiple = self.doing_multiple model = QStandardItemModel() self.filename_box.setModel(model) self.icon_file_names.sort(key=sort_key) if doing_multiple: item = QStandardItem(_('Open to see checkboxes')) item.setIcon(QIcon(I('blank.png'))) else: item = QStandardItem('') item.setFlags(Qt.ItemFlag(0)) model.appendRow(item) for i,filename in enumerate(self.icon_file_names): item = QStandardItem(filename) if doing_multiple: item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) item.setData(Qt.Unchecked, Qt.CheckStateRole) else: item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable) icon = QIcon(os.path.join(self.icon_folder, filename)) item.setIcon(icon) model.appendRow(item) def update_color_label(self): pal = QApplication.palette() bg1 = unicode_type(pal.color(pal.Base).name()) bg2 = unicode_type(pal.color(pal.AlternateBase).name()) c = self.color_box.color self.color_label.setText(''' <span style="color: {c}; background-color: {bg1}"> {st} </span> <span style="color: {c}; background-color: {bg2}"> {st} </span> '''.format(c=c, bg1=bg1, bg2=bg2, st=_('Sample text'))) def sanitize_icon_file_name(self, icon_path): n = lower(sanitize_file_name( os.path.splitext( os.path.basename(icon_path))[0]+'.png')) return n.replace("'", '_') def filename_button_clicked(self): try: path = choose_files(self, 'choose_category_icon', _('Select Icon'), filters=[ (_('Images'), ['png', 'gif', 'jpg', 'jpeg'])], all_files=False, select_only_single_file=True) if path: icon_path = path[0] icon_name = self.sanitize_icon_file_name(icon_path) if icon_name not in self.icon_file_names: self.icon_file_names.append(icon_name) try: p = QIcon(icon_path).pixmap(QSize(128, 128)) d = self.icon_folder if not os.path.exists(os.path.join(d, icon_name)): if not os.path.exists(d): os.makedirs(d) with open(os.path.join(d, icon_name), 'wb') as f: f.write(pixmap_to_data(p, format='PNG')) except: import traceback traceback.print_exc() self.update_filename_box() self.update_remove_button() if self.doing_multiple: if icon_name not in self.rule_icon_files: self.rule_icon_files.append(icon_name) self.update_icon_filenames_in_box() else: self.filename_box.setCurrentIndex(self.filename_box.findText(icon_name)) self.filename_box.adjustSize() except: import traceback traceback.print_exc() return def get_filenames_from_box(self): if self.doing_multiple: model = self.filename_box.model() fnames = [] for i in range(1, model.rowCount()): item = model.item(i, 0) if item.checkState() == Qt.Checked: fnames.append(lower(unicode_type(item.text()))) fname = ' : '.join(fnames) else: fname = lower(unicode_type(self.filename_box.currentText())) return fname def update_icon_filenames_in_box(self): if self.rule_icon_files: if not self.doing_multiple: idx = self.filename_box.findText(self.rule_icon_files[0]) if idx >= 0: self.filename_box.setCurrentIndex(idx) else: self.filename_box.setCurrentIndex(0) else: model = self.filename_box.model() for icon in self.rule_icon_files: idx = self.filename_box.findText(icon) if idx >= 0: item = model.item(idx) item.setCheckState(Qt.Checked) def update_remove_button(self): m = self.remove_button.menu() m.clear() for name in self.icon_file_names: ac = m.addAction(QIcon(os.path.join(self.icon_folder, name)), name) connect_lambda(ac.triggered, self, lambda self: self.remove_image(self.sender().text())) def remove_image(self, name): try: os.remove(os.path.join(self.icon_folder, name)) except EnvironmentError: pass else: self.populate_icon_filenames() self.update_remove_button() self.update_filename_box() self.update_icon_filenames_in_box() def add_blank_condition(self): c = ConditionEditor(self.fm, parent=self.conditions_widget) self.conditions.append(c) self.conditions_widget.layout().addWidget(c) def apply_rule(self, kind, col, rule): if kind == 'color': if rule.color: self.color_box.color = rule.color else: if self.rule_kind == 'icon': for i, tup in enumerate(icon_rule_kinds): if kind == tup[1]: self.kind_box.setCurrentIndex(i) break self.rule_icon_files = [ic.strip() for ic in rule.color.split(':')] if len(self.rule_icon_files) > 1: self.multiple_icon_cb.setChecked(True) self.update_filename_box() self.update_icon_filenames_in_box() for i in range(self.column_box.count()): c = unicode_type(self.column_box.itemData(i) or '') if col == c: self.column_box.setCurrentIndex(i) break for c in rule.conditions: ce = ConditionEditor(self.fm, parent=self.conditions_widget) self.conditions.append(ce) self.conditions_widget.layout().addWidget(ce) try: ce.condition = c except: import traceback traceback.print_exc() def accept(self): if self.rule_kind != 'color': fname = self.get_filenames_from_box() if not fname: error_dialog(self, _('No icon selected'), _('You must choose an icon for this rule'), show=True) return if self.validate(): QDialog.accept(self) def validate(self): r = Rule(self.fm) for c in self.conditions: condition = c.condition if condition is not None: try: r.add_condition(*condition) except Exception as e: import traceback error_dialog(self, _('Invalid condition'), _('One of the conditions for this rule is' ' invalid: <b>%s</b>')%e, det_msg=traceback.format_exc(), show=True) return False if len(r.conditions) < 1: error_dialog(self, _('No conditions'), _('You must specify at least one non-empty condition' ' for this rule'), show=True) return False return True @property def rule(self): r = Rule(self.fm) if self.rule_kind != 'color': r.color = self.get_filenames_from_box() else: r.color = self.color_box.color idx = self.column_box.currentIndex() col = unicode_type(self.column_box.itemData(idx) or '') for c in self.conditions: condition = c.condition if condition is not None: r.add_condition(*condition) if self.rule_kind == 'icon': kind = unicode_type(self.kind_box.itemData( self.kind_box.currentIndex()) or '') else: kind = self.rule_kind return kind, col, r
class XPathDialog(QDialog): # {{{ def __init__(self, parent, prefs): QDialog.__init__(self, parent) self.prefs = prefs self.setWindowTitle(_('Create ToC from XPath')) self.l = l = QVBoxLayout() self.setLayout(l) self.la = la = QLabel( _('Specify a series of XPath expressions for the different levels of' ' the Table of Contents. You can use the wizard buttons to help' ' you create XPath expressions.')) la.setWordWrap(True) l.addWidget(la) self.widgets = [] for i in range(5): la = _('Level %s ToC:') % ('&%d' % (i + 1)) xp = XPathEdit(self) xp.set_msg(la) self.widgets.append(xp) l.addWidget(xp) self.bb = bb = QDialogButtonBox( QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) self.ssb = b = bb.addButton(_('&Save settings'), bb.ActionRole) b.clicked.connect(self.save_settings) self.load_button = b = bb.addButton(_('&Load settings'), bb.ActionRole) self.load_menu = QMenu(b) b.setMenu(self.load_menu) self.setup_load_button() self.remove_duplicates_cb = QCheckBox( _('Do not add duplicate entries at the same level')) self.remove_duplicates_cb.setChecked( self.prefs.get('xpath_toc_remove_duplicates', True)) l.addWidget(self.remove_duplicates_cb) l.addStretch() l.addWidget(bb) self.resize(self.sizeHint() + QSize(50, 75)) def save_settings(self): xpaths = self.xpaths if not xpaths: return error_dialog(self, _('No XPaths'), _('No XPaths have been entered'), show=True) if not self.check(): return name, ok = QInputDialog.getText(self, _('Choose name'), _('Choose a name for these settings')) if ok: name = unicode_type(name).strip() if name: saved = self.prefs.get('xpath_toc_settings', {}) # in JSON all keys have to be strings saved[name] = { unicode_type(i): x for i, x in enumerate(xpaths) } self.prefs.set('xpath_toc_settings', saved) self.setup_load_button() def setup_load_button(self): saved = self.prefs.get('xpath_toc_settings', {}) m = self.load_menu m.clear() self.__actions = [] a = self.__actions.append for name in sorted(saved): a(m.addAction(name, partial(self.load_settings, name))) m.addSeparator() a(m.addAction(_('Remove saved settings'), self.clear_settings)) self.load_button.setEnabled(bool(saved)) def clear_settings(self): self.prefs.set('xpath_toc_settings', {}) self.setup_load_button() def load_settings(self, name): saved = self.prefs.get('xpath_toc_settings', {}).get(name, {}) for i, w in enumerate(self.widgets): txt = saved.get(unicode_type(i), '') w.edit.setText(txt) def check(self): for w in self.widgets: if not w.check(): error_dialog(self, _('Invalid XPath'), _('The XPath expression %s is not valid.') % w.xpath, show=True) return False return True def accept(self): if self.check(): self.prefs.set('xpath_toc_remove_duplicates', self.remove_duplicates_cb.isChecked()) super(XPathDialog, self).accept() @property def xpaths(self): return [w.xpath for w in self.widgets if w.xpath.strip()]
def ask_link(self): class Ask(QDialog): def accept(self): if self.treat_as_image.isChecked(): url = self.url.text() if url.lower().split(':', 1)[0] in ('http', 'https'): error_dialog( self, _('Remote images not supported'), _('You must download the image to your computer, URLs pointing' ' to remote images are not supported.'), show=True) return QDialog.accept(self) d = Ask(self) d.setWindowTitle(_('Create link')) l = QFormLayout() l.setFieldGrowthPolicy( QFormLayout.FieldGrowthPolicy.ExpandingFieldsGrow) d.setLayout(l) d.url = QLineEdit(d) d.name = QLineEdit(d) d.treat_as_image = QCheckBox(d) d.setMinimumWidth(600) d.bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel) d.br = b = QPushButton(_('&Browse')) b.setIcon(QIcon(I('document_open.png'))) def cf(): filetypes = [] if d.treat_as_image.isChecked(): filetypes = [(_('Images'), 'png jpeg jpg gif'.split())] files = choose_files(d, 'select link file', _('Choose file'), filetypes, select_only_single_file=True) if files: path = files[0] d.url.setText(path) if path and os.path.exists(path): with lopen(path, 'rb') as f: q = what(f) is_image = q in {'jpeg', 'png', 'gif'} d.treat_as_image.setChecked(is_image) b.clicked.connect(cf) d.la = la = QLabel( _('Enter a URL. If you check the "Treat the URL as an image" box ' 'then the URL will be added as an image reference instead of as ' 'a link. You can also choose to create a link to a file on ' 'your computer. ' 'Note that if you create a link to a file on your computer, it ' 'will stop working if the file is moved.')) la.setWordWrap(True) la.setStyleSheet('QLabel { margin-bottom: 1.5ex }') l.setWidget(0, l.SpanningRole, la) l.addRow(_('Enter &URL:'), d.url) l.addRow(_('Treat the URL as an &image'), d.treat_as_image) l.addRow(_('Enter &name (optional):'), d.name) l.addRow(_('Choose a file on your computer:'), d.br) l.addRow(d.bb) d.bb.accepted.connect(d.accept) d.bb.rejected.connect(d.reject) d.resize(d.sizeHint()) link, name, is_image = None, None, False if d.exec_() == QDialog.DialogCode.Accepted: link, name = unicode_type(d.url.text()).strip(), unicode_type( d.name.text()).strip() is_image = d.treat_as_image.isChecked() return link, name, is_image
def __init__(self, extra_customization_message, extra_customization_choices, device_settings): super(ExtraCustomization, self).__init__() debug_print( "ExtraCustomization.__init__ - extra_customization_message=", extra_customization_message) debug_print( "ExtraCustomization.__init__ - extra_customization_choices=", extra_customization_choices) debug_print( "ExtraCustomization.__init__ - device_settings.extra_customization=", device_settings.extra_customization) debug_print("ExtraCustomization.__init__ - device_settings=", device_settings) self.extra_customization_message = extra_customization_message self.l = QVBoxLayout(self) self.setLayout(self.l) options_group = QGroupBox(_("Extra driver customization options"), self) self.l.addWidget(options_group) self.extra_layout = QGridLayout() self.extra_layout.setObjectName("extra_layout") options_group.setLayout(self.extra_layout) if extra_customization_message: extra_customization_choices = extra_customization_choices or {} def parse_msg(m): msg, _, tt = m.partition(':::') if m else ('', '', '') return msg.strip(), textwrap.fill(tt.strip(), 100) if isinstance(extra_customization_message, list): self.opt_extra_customization = [] if len(extra_customization_message) > 6: row_func = lambda x, y: ((x // 2) * 2) + y col_func = lambda x: x % 2 else: row_func = lambda x, y: x * 2 + y col_func = lambda x: 0 for i, m in enumerate(extra_customization_message): label_text, tt = parse_msg(m) if not label_text: self.opt_extra_customization.append(None) continue if isinstance(device_settings.extra_customization[i], bool): self.opt_extra_customization.append( QCheckBox(label_text)) self.opt_extra_customization[-1].setToolTip(tt) self.opt_extra_customization[i].setChecked( bool(device_settings.extra_customization[i])) elif i in extra_customization_choices: cb = QComboBox(self) self.opt_extra_customization.append(cb) l = QLabel(label_text) l.setToolTip(tt), cb.setToolTip(tt), l.setBuddy( cb), cb.setToolTip(tt) for li in sorted(extra_customization_choices[i]): self.opt_extra_customization[i].addItem(li) cb.setCurrentIndex( max( 0, cb.findText( device_settings.extra_customization[i]))) else: self.opt_extra_customization.append(QLineEdit(self)) l = QLabel(label_text) l.setToolTip(tt) self.opt_extra_customization[i].setToolTip(tt) l.setBuddy(self.opt_extra_customization[i]) l.setWordWrap(True) self.opt_extra_customization[i].setText( device_settings.extra_customization[i]) self.opt_extra_customization[i].setCursorPosition(0) self.extra_layout.addWidget(l, row_func(i + 2, 0), col_func(i)) self.extra_layout.addWidget( self.opt_extra_customization[i], row_func(i + 2, 1), col_func(i)) spacerItem1 = QSpacerItem(10, 10, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding) self.extra_layout.addItem(spacerItem1, row_func(i + 2 + 2, 1), 0, 1, 2) self.extra_layout.setRowStretch(row_func(i + 2 + 2, 1), 2) else: self.opt_extra_customization = QLineEdit() label_text, tt = parse_msg(extra_customization_message) l = QLabel(label_text) l.setToolTip(tt) l.setBuddy(self.opt_extra_customization) l.setWordWrap(True) if device_settings.extra_customization: self.opt_extra_customization.setText( device_settings.extra_customization) self.opt_extra_customization.setCursorPosition(0) self.opt_extra_customization.setCursorPosition(0) self.extra_layout.addWidget(l, 0, 0) self.extra_layout.addWidget(self.opt_extra_customization, 1, 0)
class BookInfo(QDialog): closed = pyqtSignal(object) def __init__(self, parent, view, row, link_delegate): QDialog.__init__(self, parent) self.normal_brush = QBrush(Qt.white) self.marked_brush = QBrush(Qt.lightGray) self.marked = None self.gui = parent self.splitter = QSplitter(self) self._l = l = QVBoxLayout(self) self.setLayout(l) l.addWidget(self.splitter) self.cover = CoverView(self) self.cover.resizeEvent = self.cover_view_resized self.cover.cover_changed.connect(self.cover_changed) self.cover_pixmap = None self.cover.sizeHint = self.details_size_hint self.splitter.addWidget(self.cover) self.details = Details(parent.book_details.book_info, self) self.details.page().setLinkDelegationPolicy( self.details.page().DelegateAllLinks) self.details.linkClicked.connect(self.link_clicked) s = self.details.page().settings() s.setAttribute(s.JavascriptEnabled, False) self.css = css() self.link_delegate = link_delegate self.details.setAttribute(Qt.WA_OpaquePaintEvent, False) palette = self.details.palette() self.details.setAcceptDrops(False) palette.setBrush(QPalette.Base, Qt.transparent) self.details.page().setPalette(palette) self.c = QWidget(self) self.c.l = l2 = QGridLayout(self.c) self.c.setLayout(l2) l2.addWidget(self.details, 0, 0, 1, -1) self.splitter.addWidget(self.c) self.fit_cover = QCheckBox(_('Fit &cover within view'), self) self.fit_cover.setChecked( gprefs.get('book_info_dialog_fit_cover', True)) l2.addWidget(self.fit_cover, l2.rowCount(), 0, 1, -1) self.previous_button = QPushButton(QIcon(I('previous.png')), _('&Previous'), self) self.previous_button.clicked.connect(self.previous) l2.addWidget(self.previous_button, l2.rowCount(), 0) self.next_button = QPushButton(QIcon(I('next.png')), _('&Next'), self) self.next_button.clicked.connect(self.next) l2.addWidget(self.next_button, l2.rowCount() - 1, 1) self.view = view self.current_row = None self.refresh(row) self.view.model().new_bookdisplay_data.connect(self.slave) self.fit_cover.stateChanged.connect(self.toggle_cover_fit) self.ns = QShortcut(QKeySequence('Alt+Right'), self) self.ns.activated.connect(self.next) self.ps = QShortcut(QKeySequence('Alt+Left'), self) self.ps.activated.connect(self.previous) self.next_button.setToolTip( _('Next [%s]') % unicode(self.ns.key().toString(QKeySequence.NativeText))) self.previous_button.setToolTip( _('Previous [%s]') % unicode(self.ps.key().toString(QKeySequence.NativeText))) geom = QCoreApplication.instance().desktop().availableGeometry(self) screen_height = geom.height() - 100 screen_width = geom.width() - 100 self.resize(max(int(screen_width / 2), 700), screen_height) saved_layout = gprefs.get('book_info_dialog_layout', None) if saved_layout is not None: try: self.restoreGeometry(saved_layout[0]) self.splitter.restoreState(saved_layout[1]) except Exception: pass def link_clicked(self, qurl): link = unicode(qurl.toString(NO_URL_FORMATTING)) self.link_delegate(link) def done(self, r): saved_layout = (bytearray(self.saveGeometry()), bytearray(self.splitter.saveState())) gprefs.set('book_info_dialog_layout', saved_layout) ret = QDialog.done(self, r) self.view.model().new_bookdisplay_data.disconnect(self.slave) self.view = self.link_delegate = self.gui = None self.closed.emit(self) return ret def cover_changed(self, data): if self.current_row is not None: id_ = self.view.model().id(self.current_row) self.view.model().db.set_cover(id_, data) self.gui.refresh_cover_browser() ci = self.view.currentIndex() if ci.isValid(): self.view.model().current_changed(ci, ci) def details_size_hint(self): return QSize(350, 550) def toggle_cover_fit(self, state): gprefs.set('book_info_dialog_fit_cover', self.fit_cover.isChecked()) self.resize_cover() def cover_view_resized(self, event): QTimer.singleShot(1, self.resize_cover) def slave(self, mi): self.refresh(mi.row_number, mi) def move(self, delta=1): idx = self.view.currentIndex() if idx.isValid(): m = self.view.model() ni = m.index(idx.row() + delta, idx.column()) if ni.isValid(): if self.view.isVisible(): self.view.scrollTo(ni) self.view.setCurrentIndex(ni) def next(self): self.move() def previous(self): self.move(-1) def resize_cover(self): if self.cover_pixmap is None: return pixmap = self.cover_pixmap if self.fit_cover.isChecked(): scaled, new_width, new_height = fit_image( pixmap.width(), pixmap.height(), self.cover.size().width() - 10, self.cover.size().height() - 10) if scaled: try: dpr = self.devicePixelRatioF() except AttributeError: dpr = self.devicePixelRatio() pixmap = pixmap.scaled(int(dpr * new_width), int(dpr * new_height), Qt.KeepAspectRatio, Qt.SmoothTransformation) pixmap.setDevicePixelRatio(dpr) self.cover.set_pixmap(pixmap) self.update_cover_tooltip() def update_cover_tooltip(self): tt = '' if self.marked: tt = _('This book is marked') if self.marked in { True, 'true' } else _('This book is marked as: %s') % self.marked tt += '\n\n' if self.cover_pixmap is not None: sz = self.cover_pixmap.size() tt += _('Cover size: %(width)d x %(height)d') % dict( width=sz.width(), height=sz.height()) self.cover.setToolTip(tt) def refresh(self, row, mi=None): if isinstance(row, QModelIndex): row = row.row() if row == self.current_row and mi is None: return mi = self.view.model().get_book_display_info(row) if mi is None else mi if mi is None: # Indicates books was deleted from library, or row numbers have # changed return self.previous_button.setEnabled(False if row == 0 else True) self.next_button.setEnabled(False if row == self.view.model().rowCount(QModelIndex()) - 1 else True) self.current_row = row self.setWindowTitle(mi.title) self.cover_pixmap = QPixmap.fromImage(mi.cover_data[1]) try: dpr = self.devicePixelRatioF() except AttributeError: dpr = self.devicePixelRatio() self.cover_pixmap.setDevicePixelRatio(dpr) self.resize_cover() html = render_html(mi, self.css, True, self, all_fields=True) self.details.setHtml(html) self.marked = mi.marked self.cover.setBackgroundBrush( self.marked_brush if mi.marked else self.normal_brush) self.update_cover_tooltip()