class SaveOption(Option): """docstring for Save.""" saveOptionChanged = pyqtSignal(bool) def __init__(self, parent, autosaveStatus): super(SaveOption, self).__init__(parent) self.__autosaveStatus = autosaveStatus self.checkBox = QCheckBox(self) self.checkBox.setTristate(False) self.checkBox.setChecked(self.__autosaveStatus) self.checkBox.setText("Autosave") self.checkBox.setToolTip("Autosave") self.checkBox.stateChanged.connect(self.onCheckBoxStateChanged) self.addWidget(self.checkBox) def onCheckBoxStateChanged(self, state): self.saveOptionChanged.emit(self.checkBox.isChecked()) def rollback(self): self.onCheckBoxStateChanged(self.__autosaveStatus) def commit(self): self.__autosaveStatus = self.checkBox.isChecked()
class PlayerInfoField(QWidget): #Widget for inputting player info. names = ['Alex', 'Clifford', 'Tyrone', 'Ava', 'Ralph', 'Emily', 'Falcon', 'Giselle', 'Jaeger', 'Sally', 'Quentin', 'Lara'] def __init__(self, parent, index): super(PlayerInfoField, self).__init__(parent) self.index = index self.layout = QHBoxLayout() self.auto_button = Button(self, 'Auto') self.auto_button.setFixedWidth(60) self.auto_button.clicked.connect(self.generate_name) self.layout.addWidget(self.auto_button) self.name_field = QLineEdit() self.name_field.setPalette(QPalette(Qt.white)) self.name_field.setPlaceholderText('Name') self.name_field.setClearButtonEnabled(True) self.name_field.setFixedWidth(250) self.layout.addWidget(self.name_field) self.AItoggle = QCheckBox() self.AItoggle.setText('Computer') self.AItoggle.setFixedWidth(100) self.layout.addWidget(self.AItoggle) self.AItoggle.stateChanged.connect(self.AIToggled) self.AIdifficulty = QComboBox() self.AIdifficulty.setPalette(QPalette(Qt.white)) self.AIdifficulty.setFixedWidth(100) self.AIdifficulty.addItems(['Braindead', 'Easy', 'Normal', 'Hard', 'HAL-9000']) self.AIdifficulty.setCurrentIndex(2) self.AIdifficulty.setDisabled(True) self.layout.addWidget(self.AIdifficulty) self.spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.layout.addItem(self.spacer) self.layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout) def generate_name(self): self.name_field.setText(PlayerInfoField.names[self.index]) def AIToggled(self): if self.AItoggle.checkState() == Qt.Checked: self.AIdifficulty.setEnabled(True) else: self.AIdifficulty.setDisabled(True)
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"))
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"))
class PopOut(QDialog): ''' Pop Out para modificar ou adicionar nome de um elemento na arvore ''' def __init__(self, name, bOK=False): super().__init__() self.items = None, None self.setWindowFlags(Qt.FramelessWindowHint) self.lineEdit = QLineEdit(name) self.chbStore = QCheckBox() self.chbStore.setText("Guardar dados") self.chbStore.setChecked(bOK) self.pbOK = QPushButton() self.pbOK.setText("OK") self.pbOK.clicked.connect(self.getNewName) self.pbCancel = QPushButton() self.pbCancel.setText("Cancel") self.pbCancel.clicked.connect(self.close) layout = QVBoxLayout() layout.addWidget(self.lineEdit) layout.addWidget(self.chbStore) layout.addWidget(self.pbOK) layout.addWidget(self.pbCancel) self.setLayout(layout) def getNewName(self): nome = self.lineEdit.text() toStore = self.chbStore.isChecked() self.items = (nome, toStore) self.close()
class JobError(QDialog): # {{{ WIDTH = 600 do_pop = pyqtSignal() def __init__(self, parent): QDialog.__init__(self, parent) self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, False) self.queue = [] self.do_pop.connect(self.pop, type=Qt.ConnectionType.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.TextFormat.RichText) self.det_msg = QPlainTextEdit(self) self.det_msg.setVisible(False) self.bb = QDialogButtonBox(QDialogButtonBox.StandardButton.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.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignBottom) l.addWidget(self.bb, 3, 0, 1, 2, Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.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( 'calibre, version %s (%s, embedded-python: %s)\n%s: %s\n\n%s' % (__version__, sys.platform, isfrozen, unicode_type(self.windowTitle()), unicode_type( d.toPlainText()), unicode_type(self.det_msg.toPlainText()))) if hasattr(self, 'ctc_button'): self.ctc_button.setText(_('Copied')) def toggle_det_msg(self, *args): vis = unicode_type(self.det_msg_toggle.text()) == self.hide_det_msg self.det_msg_toggle.setText( self.show_det_msg if vis else self.hide_det_msg) self.det_msg.setVisible(not vis) self.do_resize() def do_resize(self): 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.FocusReason.OtherFocusReason) return ret def show_error(self, title, msg, det_msg='', 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 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)
class Config(QDialog): ''' Configuration dialog for single book conversion. If accepted, has the following important attributes output_format - Output format (without a leading .) input_format - Input format (without a leading .) opf_path - Path to OPF file with user specified metadata cover_path - Path to user specified cover (can be None) recommendations - A pickled list of 3 tuples in the same format as the recommendations member of the Input/Output plugins. ''' def __init__(self, parent, db, book_id, preferred_input_format=None, preferred_output_format=None): QDialog.__init__(self, parent) self.setupUi() self.opt_individual_saved_settings.setVisible(False) self.db, self.book_id = db, book_id self.setup_input_output_formats(self.db, self.book_id, preferred_input_format, preferred_output_format) self.setup_pipeline() self.input_formats.currentIndexChanged[native_string_type].connect( self.setup_pipeline) self.output_formats.currentIndexChanged[native_string_type].connect( self.setup_pipeline) self.groups.setSpacing(5) self.groups.activated[(QModelIndex)].connect(self.show_pane) self.groups.clicked[(QModelIndex)].connect(self.show_pane) self.groups.entered[(QModelIndex)].connect(self.show_group_help) rb = self.buttonBox.button(self.buttonBox.RestoreDefaults) rb.setText(_('Restore &defaults')) rb.clicked.connect(self.restore_defaults) self.groups.setMouseTracking(True) geom = gprefs.get('convert_single_dialog_geom', None) if geom: self.restoreGeometry(geom) else: self.resize(self.sizeHint()) def setupUi(self): self.setObjectName("Dialog") self.resize(1024, 700) self.setWindowIcon(QIcon(I('convert.png'))) self.gridLayout = QGridLayout(self) self.gridLayout.setObjectName("gridLayout") self.horizontalLayout = QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") self.input_label = QLabel(self) self.input_label.setObjectName("input_label") self.horizontalLayout.addWidget(self.input_label) self.input_formats = QComboBox(self) self.input_formats.setSizeAdjustPolicy( QComboBox.AdjustToMinimumContentsLengthWithIcon) self.input_formats.setMinimumContentsLength(5) self.input_formats.setObjectName("input_formats") self.horizontalLayout.addWidget(self.input_formats) self.opt_individual_saved_settings = QCheckBox(self) self.opt_individual_saved_settings.setObjectName( "opt_individual_saved_settings") self.horizontalLayout.addWidget(self.opt_individual_saved_settings) spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout.addItem(spacerItem) self.label_2 = QLabel(self) self.label_2.setObjectName("label_2") self.horizontalLayout.addWidget(self.label_2) self.output_formats = QComboBox(self) self.output_formats.setSizeAdjustPolicy( QComboBox.AdjustToMinimumContentsLengthWithIcon) self.output_formats.setMinimumContentsLength(5) self.output_formats.setObjectName("output_formats") self.horizontalLayout.addWidget(self.output_formats) self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 2) self.groups = QListView(self) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(1) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.groups.sizePolicy().hasHeightForWidth()) self.groups.setSizePolicy(sizePolicy) self.groups.setTabKeyNavigation(True) self.groups.setIconSize(QSize(48, 48)) self.groups.setWordWrap(True) self.groups.setObjectName("groups") self.gridLayout.addWidget(self.groups, 1, 0, 3, 1) self.scrollArea = QScrollArea(self) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(4) sizePolicy.setVerticalStretch(10) sizePolicy.setHeightForWidth( self.scrollArea.sizePolicy().hasHeightForWidth()) self.scrollArea.setSizePolicy(sizePolicy) self.scrollArea.setFrameShape(QFrame.NoFrame) self.scrollArea.setLineWidth(0) self.scrollArea.setWidgetResizable(True) self.scrollArea.setObjectName("scrollArea") self.scrollAreaWidgetContents = QWidget() self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 810, 494)) self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") self.verticalLayout_3 = QVBoxLayout(self.scrollAreaWidgetContents) self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) self.verticalLayout_3.setObjectName("verticalLayout_3") self.stack = QStackedWidget(self.scrollAreaWidgetContents) sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.stack.sizePolicy().hasHeightForWidth()) self.stack.setSizePolicy(sizePolicy) self.stack.setObjectName("stack") self.page = QWidget() self.page.setObjectName("page") self.stack.addWidget(self.page) self.page_2 = QWidget() self.page_2.setObjectName("page_2") self.stack.addWidget(self.page_2) self.verticalLayout_3.addWidget(self.stack) self.scrollArea.setWidget(self.scrollAreaWidgetContents) self.gridLayout.addWidget(self.scrollArea, 1, 1, 1, 1) self.buttonBox = QDialogButtonBox(self) self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok | QDialogButtonBox.RestoreDefaults) self.buttonBox.setObjectName("buttonBox") self.gridLayout.addWidget(self.buttonBox, 3, 1, 1, 1) self.help = QTextEdit(self) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.help.sizePolicy().hasHeightForWidth()) self.help.setSizePolicy(sizePolicy) self.help.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 i in range(self.stack.count()): w = self.stack.widget(i) if hasattr(w, 'manually_fine_tune_toc'): return w.manually_fine_tune_toc.isChecked() def setup_pipeline(self, *args): oidx = self.groups.currentIndex().row() input_format = self.input_format output_format = self.output_format self.plumber = create_dummy_plumber(input_format, output_format) def widget_factory(cls): return cls(self.stack, self.plumber.get_option_by_name, self.plumber.get_option_help, self.db, self.book_id) self.mw = widget_factory(MetadataWidget) self.setWindowTitle( _('Convert') + ' ' + unicode_type(self.mw.title.text())) lf = widget_factory(LookAndFeelWidget) hw = widget_factory(HeuristicsWidget) sr = widget_factory(SearchAndReplaceWidget) ps = widget_factory(PageSetupWidget) sd = widget_factory(StructureDetectionWidget) toc = widget_factory(TOCWidget) from calibre.gui2.actions.toc_edit import SUPPORTED toc.manually_fine_tune_toc.setVisible( output_format.upper() in SUPPORTED) debug = widget_factory(DebugWidget) output_widget = self.plumber.output_plugin.gui_configuration_widget( self.stack, self.plumber.get_option_by_name, self.plumber.get_option_help, self.db, self.book_id) input_widget = self.plumber.input_plugin.gui_configuration_widget( self.stack, self.plumber.get_option_by_name, self.plumber.get_option_help, self.db, self.book_id) while True: c = self.stack.currentWidget() if not c: break self.stack.removeWidget(c) widgets = [self.mw, lf, hw, ps, sd, toc, sr] if input_widget is not None: widgets.append(input_widget) if output_widget is not None: widgets.append(output_widget) widgets.append(debug) for w in widgets: self.stack.addWidget(w) w.set_help_signal.connect(self.help.setPlainText) self._groups_model = GroupModel(widgets) self.groups.setModel(self._groups_model) idx = oidx if -1 < oidx < self._groups_model.rowCount() else 0 self.groups.setCurrentIndex(self._groups_model.index(idx)) self.stack.setCurrentIndex(idx) try: shutil.rmtree(self.plumber.archive_input_tdir, ignore_errors=True) except: pass def setup_input_output_formats(self, db, book_id, preferred_input_format, preferred_output_format): if preferred_output_format: preferred_output_format = preferred_output_format.upper() output_formats = get_output_formats(preferred_output_format) input_format, input_formats = get_input_format_for_book( db, book_id, preferred_input_format) preferred_output_format = preferred_output_format if \ preferred_output_format in output_formats else \ sort_formats_by_preference(output_formats, [prefs['output_format']])[0] self.input_formats.addItems( (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): self.stack.setCurrentIndex(index.row()) def accept(self): recs = GuiRecommendations() for w in self._groups_model.widgets: if not w.pre_commit_check(): return x = w.commit(save_defaults=False) recs.update(x) self.opf_file, self.cover_file = self.mw.opf_file, self.mw.cover_file self._recommendations = recs if self.db is not None: recs['gui_preferred_input_format'] = self.input_format save_specifics(self.db, self.book_id, recs) self.break_cycles() QDialog.accept(self) def reject(self): self.break_cycles() QDialog.reject(self) def done(self, r): if self.isVisible(): gprefs['convert_single_dialog_geom'] = \ bytearray(self.saveGeometry()) return QDialog.done(self, r) def break_cycles(self): for i in range(self.stack.count()): w = self.stack.widget(i) w.break_cycles() @property def recommendations(self): recs = [(k, v, OptionRecommendation.HIGH) for k, v in self._recommendations.items()] return recs def show_group_help(self, index): widget = self._groups_model.widgets[index.row()] self.help.setPlainText(widget.HELP)
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_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 retry(self): if self.retry_func is not None: self.accept() self.retry_func() def update_suppress_state(self): self.suppress.setText(_( 'Hide the remaining %d error messages'%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 ProceedQuestion(QWidget): ask_question = pyqtSignal(object, object, object) @pyqtProperty(float) def show_fraction(self): return self._show_fraction @show_fraction.setter def show_fraction(self, val): self._show_fraction = max(0, min(1, float(val))) self.update() def __init__(self, parent): QWidget.__init__(self, parent) self.setVisible(False) parent.installEventFilter(self) self._show_fraction = 0.0 self.show_animation = a = QPropertyAnimation(self, b"show_fraction", self) a.setDuration(1000), a.setEasingCurve(QEasingCurve.OutQuad) a.setStartValue(0.0), a.setEndValue(1.0) a.finished.connect(self.stop_show_animation) self.rendered_pixmap = None self.questions = [] self.icon = ic = Icon(self) self.msg_label = msg = QLabel('some random filler text') msg.setWordWrap(True) self.bb = QDialogButtonBox() self.bb.accepted.connect(self.accept) self.bb.rejected.connect(self.reject) self.log_button = self.bb.addButton(_('View log'), self.bb.ActionRole) self.log_button.setIcon(QIcon(I('debug.png'))) self.log_button.clicked.connect(self.show_log) self.copy_button = self.bb.addButton(_('&Copy to clipboard'), self.bb.ActionRole) self.copy_button.clicked.connect(self.copy_to_clipboard) self.action_button = self.bb.addButton('', self.bb.ActionRole) self.action_button.clicked.connect(self.action_clicked) self.show_det_msg = _('Show &details') self.hide_det_msg = _('Hide &details') self.det_msg_toggle = self.bb.addButton(self.show_det_msg, self.bb.ActionRole) self.det_msg_toggle.clicked.connect(self.toggle_det_msg) self.det_msg_toggle.setToolTip( _('Show detailed information about this error')) self.det_msg = PlainTextEdit(self) self.det_msg.setReadOnly(True) self.bb.setStandardButtons(self.bb.Yes | self.bb.No | self.bb.Ok) self.bb.button(self.bb.Yes).setDefault(True) self.title_label = title = QLabel('A dummy title') f = title.font() f.setBold(True) title.setFont(f) self.checkbox = QCheckBox('', self) self._l = l = QVBoxLayout(self) self._h = h = QHBoxLayout() self._v = v = QVBoxLayout() v.addWidget(title), v.addWidget(msg) h.addWidget(ic), h.addSpacing(10), h.addLayout(v), l.addLayout(h) l.addSpacing(5) l.addWidget(self.checkbox) l.addWidget(self.det_msg) l.addWidget(self.bb) self.ask_question.connect(self.do_ask_question, type=Qt.QueuedConnection) self.setFocusPolicy(Qt.NoFocus) for child in self.findChildren(QWidget): child.setFocusPolicy(Qt.NoFocus) self.setFocusProxy(self.parent()) self.resize_timer = t = QTimer(self) t.setSingleShot(True), t.setInterval(100), t.timeout.connect( self.parent_resized) def eventFilter(self, obj, ev): if ev.type() == ev.Resize and self.isVisible(): self.resize_timer.start() return False def parent_resized(self): if self.isVisible(): self.do_resize() def copy_to_clipboard(self, *args): QApplication.clipboard().setText( 'calibre, version %s\n%s: %s\n\n%s' % (__version__, unicode_type( self.windowTitle()), unicode_type(self.msg_label.text()), unicode_type(self.det_msg.toPlainText()))) self.copy_button.setText(_('Copied')) def action_clicked(self): if self.questions: q = self.questions[0] self.questions[0] = q._replace(callback=q.action_callback) self.accept() def accept(self): if self.questions: payload, callback, cancel_callback = self.questions[0][:3] self.questions = self.questions[1:] cb = None if self.checkbox.isVisible(): cb = bool(self.checkbox.isChecked()) self.ask_question.emit(callback, payload, cb) self.hide() def reject(self): if self.questions: payload, callback, cancel_callback = self.questions[0][:3] self.questions = self.questions[1:] cb = None if self.checkbox.isVisible(): cb = bool(self.checkbox.isChecked()) self.ask_question.emit(cancel_callback, payload, cb) self.hide() def do_ask_question(self, callback, payload, checkbox_checked): if callable(callback): args = [payload] if checkbox_checked is not None: args.append(checkbox_checked) callback(*args) self.show_question() def toggle_det_msg(self, *args): vis = unicode_type(self.det_msg_toggle.text()) == self.hide_det_msg self.det_msg_toggle.setText( self.show_det_msg if vis else self.hide_det_msg) self.det_msg.setVisible(not vis) self.do_resize() def do_resize(self): sz = self.sizeHint() sz.setWidth(min(self.parent().width(), sz.width())) sb = self.parent().statusBar().height() + 10 sz.setHeight(min(self.parent().height() - sb, sz.height())) self.resize(sz) self.position_widget() def show_question(self): if not self.questions: return if not self.isVisible(): question = self.questions[0] self.msg_label.setText(question.msg) self.icon.set_icon(question.icon) self.title_label.setText(question.title) self.log_button.setVisible(bool(question.html_log)) self.copy_button.setText(_('&Copy to clipboard')) if question.action_callback is not None: self.action_button.setText(question.action_label or '') self.action_button.setIcon(QIcon( ) if question.action_icon is None else question.action_icon) # Force the button box to relayout its buttons, as button text # might have changed self.bb.setOrientation(Qt.Vertical), self.bb.setOrientation( Qt.Horizontal) self.det_msg.setPlainText(question.det_msg or '') self.det_msg.setVisible(False) self.det_msg_toggle.setVisible(bool(question.det_msg)) self.det_msg_toggle.setText(self.show_det_msg) self.checkbox.setVisible(question.checkbox_msg is not None) if question.checkbox_msg is not None: self.checkbox.setText(question.checkbox_msg) self.checkbox.setChecked(question.checkbox_checked) self.bb.button(self.bb.Ok).setVisible(question.show_ok) self.bb.button(self.bb.Yes).setVisible(not question.show_ok) self.bb.button(self.bb.No).setVisible(not question.show_ok) self.copy_button.setVisible(bool(question.show_copy_button)) self.action_button.setVisible(question.action_callback is not None) self.toggle_det_msg() if question.show_det else self.do_resize() self.show_widget() button = self.action_button if question.focus_action and question.action_callback is not None else \ (self.bb.button(self.bb.Ok) if question.show_ok else self.bb.button(self.bb.Yes)) button.setDefault(True) self.raise_() self.start_show_animation() def start_show_animation(self): if self.rendered_pixmap is not None: return dpr = getattr(self, 'devicePixelRatioF', self.devicePixelRatio)() p = QImage(dpr * self.size(), QImage.Format_ARGB32_Premultiplied) p.setDevicePixelRatio(dpr) # For some reason, Qt scrolls the book view when rendering this widget, # for the very first time, so manually preserve its position pr = getattr(self.parent(), 'library_view', None) if not hasattr(pr, 'preserve_state'): self.render(p) else: with pr.preserve_state(): self.render(p) self.rendered_pixmap = QPixmap.fromImage(p) self.original_visibility = v = [] for child in self.findChildren(QWidget): if child.isVisible(): child.setVisible(False) v.append(child) self.show_animation.start() def stop_show_animation(self): self.rendered_pixmap = None [c.setVisible(True) for c in getattr(self, 'original_visibility', ())] self.update() for child in self.findChildren(QWidget): child.update() if hasattr(child, 'viewport'): child.viewport().update() def position_widget(self): geom = self.parent().geometry() x = geom.width() - self.width() - 5 sb = self.parent().statusBar() if sb is None: y = geom.height() - self.height() else: y = sb.geometry().top() - self.height() self.move(x, y) def show_widget(self): self.show() self.position_widget() def dummy_question(self, action_label=None): self( lambda *args: args, (), 'dummy log', 'Log Viewer', 'A Dummy Popup', 'This is a dummy popup to easily test things, with a long line of text that should wrap. ' 'This is a dummy popup to easily test things, with a long line of text that should wrap', checkbox_msg='A dummy checkbox', action_callback=lambda *args: args, action_label=action_label or 'An action') def __call__(self, callback, payload, html_log, log_viewer_title, title, msg, det_msg='', show_copy_button=False, cancel_callback=None, log_is_file=False, checkbox_msg=None, checkbox_checked=False, action_callback=None, action_label=None, action_icon=None, focus_action=False, show_det=False, show_ok=False, icon=None, log_viewer_unique_name=None, **kw): ''' A non modal popup that notifies the user that a background task has been completed. This class guarantees that only a single popup is visible at any one time. Other requests are queued and displayed after the user dismisses the current popup. :param callback: A callable that is called with payload if the user asks to proceed. Note that this is always called in the GUI thread. :param cancel_callback: A callable that is called with the payload if the users asks not to proceed. :param payload: Arbitrary object, passed to callback :param html_log: An HTML or plain text log :param log_viewer_title: The title for the log viewer window :param title: The title for this popup :param msg: The msg to display :param det_msg: Detailed message :param log_is_file: If True the html_log parameter is interpreted as the path to a file on disk containing the log encoded with utf-8 :param checkbox_msg: If not None, a checkbox is displayed in the dialog, showing this message. The callback is called with both the payload and the state of the checkbox as arguments. :param checkbox_checked: If True the checkbox is checked by default. :param action_callback: If not None, an extra button is added, which when clicked will cause action_callback to be called instead of callback. action_callback is called in exactly the same way as callback. :param action_label: The text on the action button :param action_icon: The icon for the action button, must be a QIcon object or None :param focus_action: If True, the action button will be focused instead of the Yes button :param show_det: If True, the Detailed message will be shown initially :param show_ok: If True, OK will be shown instead of YES/NO :param icon: The icon to be used for this popop (defaults to question mark). Can be either a QIcon or a string to be used with I() :log_viewer_unique_name: If set, ViewLog will remember/reuse its size for this name in calibre.gui2.gprefs ''' question = Question(payload, callback, cancel_callback, title, msg, html_log, log_viewer_title, log_is_file, det_msg, show_copy_button, checkbox_msg, checkbox_checked, action_callback, action_label, action_icon, focus_action, show_det, show_ok, icon, log_viewer_unique_name) self.questions.append(question) self.show_question() def show_log(self): if self.questions: q = self.questions[0] log = q.html_log if q.log_is_file: with open(log, 'rb') as f: log = f.read().decode('utf-8') self.log_viewer = ViewLog(q.log_viewer_title, log, parent=self, unique_name=q.log_viewer_unique_name) def paintEvent(self, ev): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.SmoothPixmapTransform, True) try: if self.rendered_pixmap is None: self.paint_background(painter) else: self.animated_paint(painter) finally: painter.end() def animated_paint(self, painter): top = (1 - self._show_fraction) * self.height() painter.drawPixmap(0, top, self.rendered_pixmap) def paint_background(self, painter): br = 12 # border_radius bw = 1 # border_width pal = self.palette() c = pal.color(pal.Window) c.setAlphaF(0.9) p = QPainterPath() p.addRoundedRect(QRectF(self.rect()), br, br) painter.fillPath(p, c) p.addRoundedRect( QRectF(self.rect()).adjusted(bw, bw, -bw, -bw), br, br) painter.fillPath(p, pal.color(pal.WindowText))
class MakeBrickDialog(QDialog): def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.model = None self._model_dir = None self.setModal(modal) self.setWindowTitle("Convert sources to FITS brick") lo = QVBoxLayout(self) lo.setContentsMargins(10, 10, 10, 10) lo.setSpacing(5) # file selector self.wfile = FileSelector(self, label="FITS filename:", dialog_label="Output FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.ExistingFile) lo.addWidget(self.wfile) # reference frequency lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) label = QLabel("Frequency, MHz:", self) lo1.addWidget(label) tip = """<P>If your sky model contains spectral information (such as spectral indices), then a brick may be generated for a specific frequency. If a frequency is not specified here, the reference frequency of the model sources will be assumed.</P>""" self.wfreq = QLineEdit(self) self.wfreq.setValidator(QDoubleValidator(self)) label.setToolTip(tip) self.wfreq.setToolTip(tip) lo1.addWidget(self.wfreq) # beam gain lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.wpb_apply = QCheckBox("Apply primary beam expression:", self) self.wpb_apply.setChecked(True) lo1.addWidget(self.wpb_apply) tip = """<P>If this option is specified, a primary power beam gain will be applied to the sources before inserting them into the brick. This can be any valid Python expression making use of the variables 'r' (corresponding to distance from field centre, in radians) and 'fq' (corresponding to frequency.)</P>""" self.wpb_exp = QLineEdit(self) self.wpb_apply.setToolTip(tip) self.wpb_exp.setToolTip(tip) lo1.addWidget(self.wpb_exp) # overwrite or add mode lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.woverwrite = QRadioButton("overwrite image", self) self.woverwrite.setChecked(True) lo1.addWidget(self.woverwrite) self.waddinto = QRadioButton("add into image", self) lo1.addWidget(self.waddinto) # add to model self.wadd = QCheckBox( "Add resulting brick to sky model as a FITS image component", self) lo.addWidget(self.wadd) lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.wpad = QLineEdit(self) self.wpad.setValidator(QDoubleValidator(self)) self.wpad.setText("1.1") lab = QLabel("...with padding factor:", self) lab.setToolTip( """<P>The padding factor determines the amount of null padding inserted around the image during the prediction stage. Padding alleviates the effects of tapering and detapering in the uv-brick, which can show up towards the edges of the image. For a factor of N, the image will be padded out to N times its original size. This increases memory use, so if you have no flux at the edges of the image anyway, then a pad factor of 1 is perfectly fine.</P>""") self.wpad.setToolTip(lab.toolTip()) self.wadd.toggled[bool].connect(self.wpad.setEnabled) self.wadd.toggled[bool].connect(lab.setEnabled) self.wpad.setEnabled(False) lab.setEnabled(False) lo1.addStretch(1) lo1.addWidget(lab, 0) lo1.addWidget(self.wpad, 1) self.wdel = QCheckBox( "Remove from the sky model sources that go into the brick", self) lo.addWidget(self.wdel) # OK/cancel buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) 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.filenameSelected.connect(self._fileSelected) # internal state self.qerrmsg = QErrorMessage(self) def setModel(self, model): self.model = model pb = self.model.primaryBeam() if pb: self.wpb_exp.setText(pb) else: self.wpb_apply.setChecked(False) self.wpb_exp.setText("") if model.filename(): self._model_dir = os.path.dirname(os.path.abspath( model.filename())) else: self._model_dir = os.path.abspath('.') self.wfile.setDirectory(self._model_dir) self._fileSelected(self.wfile.filename(), quiet=True) def _fileSelected(self, filename, quiet=False): self.wokbtn.setEnabled(False) if not filename: return None # check that filename matches model if not os.path.samefile(self._model_dir, os.path.dirname(filename)): self.wfile.setFilename('') if not quiet: QMessageBox.warning( self, "Directory mismatch", """<P>The FITS file must reside in the same directory as the current sky model.</P>""") self.wfile.setDirectory(self._model_dir) return None # read fits file busy = BusyIndicator() try: input_hdu = pyfits.open(filename)[0] hdr = input_hdu.header # get frequency, if specified for axis in range(1, hdr['NAXIS'] + 1): if hdr['CTYPE%d' % axis].upper() == 'FREQ': self.wfreq.setText(str(hdr['CRVAL%d' % axis] / 1e+6)) break except Exception as err: busy.reset_cursor() self.wfile.setFilename('') if not quiet: QMessageBox.warning( self, "Error reading FITS", "Error reading FITS file %s: %s" % (filename, str(err))) return None self.wokbtn.setEnabled(True) # if filename is not in model already, enable the "add to model" control for src in self.model.sources: if isinstance(getattr(src, 'shape', None), ModelClasses.FITSImage) \ and os.path.exists(src.shape.filename) and os.path.exists(filename) \ and os.path.samefile(src.shape.filename, filename): self.wadd.setChecked(True) self.wadd.setEnabled(False) self.wadd.setText("image already in sky model") break else: self.wadd.setText("add image to sky model") busy.reset_cursor() return filename def accept(self): """Tries to make a brick, and closes the dialog if successful.""" sources = [ src for src in self.model.sources if src.selected and src.typecode == 'pnt' ] filename = self.wfile.filename() if not self._fileSelected(filename): return # get PB expression pbfunc = None if self.wpb_apply.isChecked(): pbexp = str(self.wpb_exp.text()) try: pbfunc = eval("lambda r,fq:" + pbexp) except Exception as err: QMessageBox.warning( self, "Error parsing PB experssion", "Error parsing primary beam expression %s: %s" % (pbexp, str(err))) return # get frequency freq = str(self.wfreq.text()) freq = float(freq) * 1e+6 if freq else None # get pad factor pad = str(self.wpad.text()) pad = max(float(pad), 1) if pad else 1 # read fits file busy = BusyIndicator() try: input_hdu = pyfits.open(filename)[0] except Exception as err: busy.reset_cursor() QMessageBox.warning( self, "Error reading FITS", "Error reading FITS file %s: %s" % (filename, str(err))) return # reset data if asked to if self.woverwrite.isChecked(): input_hdu.data[...] = 0 # insert sources Imaging.restoreSources(input_hdu, sources, 0, primary_beam=pbfunc, freq=freq) # save fits file try: # pyfits seems to produce an exception: # TypeError: formatwarning() takes exactly 4 arguments (5 given) # when attempting to overwrite a file. As a workaround, remove the file first. if os.path.exists(filename): os.remove(filename) input_hdu.writeto(filename) except Exception as err: traceback.print_exc() busy.reset_cursor() QMessageBox.warning( self, "Error writing FITS", "Error writing FITS file %s: %s" % (filename, str(err))) return changed = False sources = self.model.sources # remove sources from model if asked to if self.wdel.isChecked(): sources = [ src for src in sources if not (src.selected and src.typecode == 'pnt') ] changed = True # add image to model if asked to if self.wadd.isChecked(): hdr = input_hdu.header # get image parameters max_flux = float(input_hdu.data.max()) wcs = WCS(hdr, mode='pyfits') # Get reference pixel coordinates # wcs.getCentreWCSCoords() doesn't work, as that gives us the middle of the image # So scan the header to get the CRPIX values ra0 = dec0 = 1 for iaxis in range(hdr['NAXIS']): axs = str(iaxis + 1) name = hdr.get('CTYPE' + axs, axs).upper() if name.startswith("RA"): ra0 = hdr.get('CRPIX' + axs, 1) - 1 elif name.startswith("DEC"): dec0 = hdr.get('CRPIX' + axs, 1) - 1 # convert pixel to degrees ra0, dec0 = wcs.pix2wcs(ra0, dec0) ra0 *= DEG dec0 *= DEG sx, sy = wcs.getHalfSizeDeg() sx *= DEG sy *= DEG nx, ny = input_hdu.data.shape[-1:-3:-1] # check if this image is already contained in the model for src in sources: if isinstance(getattr(src, 'shape', None), ModelClasses.FITSImage) and os.path.samefile( src.shape.filename, filename): # update source parameters src.pos.ra, src.pos.dec = ra0, dec0 src.flux.I = max_flux src.shape.ex, src.shape.ey = sx, sy src.shape.nx, src.shape.ny = nx, ny src.shape.pad = pad break # not contained, make new source object else: pos = ModelClasses.Position(ra0, dec0) flux = ModelClasses.Flux(max_flux) shape = ModelClasses.FITSImage(sx, sy, 0, os.path.basename(filename), nx, ny, pad=pad) img_src = SkyModel.Source(os.path.splitext( os.path.basename(filename))[0], pos, flux, shape=shape) sources.append(img_src) changed = True if changed: self.model.setSources(sources) self.model.emitUpdate(SkyModel.SkyModel.UpdateAll, origin=self) self.parent().showMessage("Wrote %d sources to FITS file %s" % (len(sources), filename)) busy.reset_cursor() return QDialog.accept(self)
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)
class ProceedQuestion(QDialog): ask_question = pyqtSignal(object, object, object) def __init__(self, parent): QDialog.__init__(self, parent) self.setAttribute(Qt.WA_DeleteOnClose, False) self.setWindowIcon(QIcon(I('dialog_question.png'))) self.questions = [] self._l = l = QGridLayout(self) self.setLayout(l) self.icon_label = ic = QLabel(self) ic.setPixmap(QPixmap(I('dialog_question.png'))) self.msg_label = msg = QLabel('some random filler text') msg.setWordWrap(True) ic.setMaximumWidth(110) ic.setMaximumHeight(100) ic.setScaledContents(True) ic.setStyleSheet('QLabel { margin-right: 10px }') self.bb = QDialogButtonBox() self.bb.accepted.connect(self.accept) self.bb.rejected.connect(self.reject) self.log_button = self.bb.addButton(_('View log'), self.bb.ActionRole) self.log_button.setIcon(QIcon(I('debug.png'))) self.log_button.clicked.connect(self.show_log) self.copy_button = self.bb.addButton(_('&Copy to clipboard'), self.bb.ActionRole) self.copy_button.clicked.connect(self.copy_to_clipboard) self.action_button = self.bb.addButton('', self.bb.ActionRole) self.action_button.clicked.connect(self.action_clicked) self.show_det_msg = _('Show &details') self.hide_det_msg = _('Hide &details') self.det_msg_toggle = self.bb.addButton(self.show_det_msg, self.bb.ActionRole) self.det_msg_toggle.clicked.connect(self.toggle_det_msg) self.det_msg_toggle.setToolTip( _('Show detailed information about this error')) self.det_msg = QPlainTextEdit(self) self.det_msg.setReadOnly(True) self.bb.setStandardButtons(self.bb.Yes|self.bb.No|self.bb.Ok) self.bb.button(self.bb.Yes).setDefault(True) self.checkbox = QCheckBox('', self) l.addWidget(ic, 0, 0, 1, 1) l.addWidget(msg, 0, 1, 1, 1) l.addWidget(self.checkbox, 1, 0, 1, 2) l.addWidget(self.det_msg, 2, 0, 1, 2) l.addWidget(self.bb, 3, 0, 1, 2) self.ask_question.connect(self.do_ask_question, type=Qt.QueuedConnection) def copy_to_clipboard(self, *args): QApplication.clipboard().setText( 'calibre, version %s\n%s: %s\n\n%s' % (__version__, unicode(self.windowTitle()), unicode(self.msg_label.text()), unicode(self.det_msg.toPlainText()))) self.copy_button.setText(_('Copied')) def action_clicked(self): if self.questions: q = self.questions[0] self.questions[0] = q._replace(callback=q.action_callback) self.accept() def accept(self): if self.geom_pref: gprefs[self.geom_pref] = bytearray(self.saveGeometry()) if self.questions: payload, callback, cancel_callback = self.questions[0][:3] self.questions = self.questions[1:] cb = None if self.checkbox.isVisible(): cb = bool(self.checkbox.isChecked()) self.ask_question.emit(callback, payload, cb) self.hide() def reject(self): if self.geom_pref: gprefs[self.geom_pref] = bytearray(self.saveGeometry()) if self.questions: payload, callback, cancel_callback = self.questions[0][:3] self.questions = self.questions[1:] cb = None if self.checkbox.isVisible(): cb = bool(self.checkbox.isChecked()) self.ask_question.emit(cancel_callback, payload, cb) self.hide() def do_ask_question(self, callback, payload, checkbox_checked): if callable(callback): args = [payload] if checkbox_checked is not None: args.append(checkbox_checked) callback(*args) self.show_question() def toggle_det_msg(self, *args): vis = unicode(self.det_msg_toggle.text()) == self.hide_det_msg self.det_msg_toggle.setText(self.show_det_msg if vis else self.hide_det_msg) self.det_msg.setVisible(not vis) self.do_resize() def do_resize(self): if self.geom_pref: geom = gprefs.get(self.geom_pref, None) if geom: self.restoreGeometry(geom) return sz = self.sizeHint() + QSize(100, 0) sz.setWidth(min(500, sz.width())) sz.setHeight(min(500, sz.height())) self.resize(sz) def show_question(self): if self.isVisible(): return if self.questions: question = self.questions[0] self.msg_label.setText(question.msg) self.setWindowTitle(question.title) self.log_button.setVisible(bool(question.html_log)) self.copy_button.setText(_('&Copy to clipboard')) self.copy_button.setVisible(bool(question.show_copy_button)) self.action_button.setVisible(question.action_callback is not None) if question.action_callback is not None: self.action_button.setText(question.action_label or '') self.action_button.setIcon( QIcon() if question.action_icon is None else question.action_icon) self.det_msg.setPlainText(question.det_msg or '') self.det_msg.setVisible(False) self.det_msg_toggle.setVisible(bool(question.det_msg)) self.det_msg_toggle.setText(self.show_det_msg) self.checkbox.setVisible(question.checkbox_msg is not None) if question.checkbox_msg is not None: self.checkbox.setText(question.checkbox_msg) self.checkbox.setChecked(question.checkbox_checked) self.bb.button(self.bb.Ok).setVisible(question.show_ok) self.bb.button(self.bb.Yes).setVisible(not question.show_ok) self.bb.button(self.bb.No).setVisible(not question.show_ok) self.geom_pref = ('proceed question dialog:' + question.geom_pref) if question.geom_pref else None if question.show_det: self.toggle_det_msg() else: self.do_resize() self.show() button = self.action_button if question.focus_action and question.action_callback is not None else \ (self.bb.button(self.bb.Ok) if question.show_ok else self.bb.button(self.bb.Yes)) button.setDefault(True) button.setFocus(Qt.OtherFocusReason) def __call__(self, callback, payload, html_log, log_viewer_title, title, msg, det_msg='', show_copy_button=False, cancel_callback=None, log_is_file=False, checkbox_msg=None, checkbox_checked=False, action_callback=None, action_label=None, action_icon=None, focus_action=False, show_det=False, show_ok=False, geom_pref=None): ''' A non modal popup that notifies the user that a background task has been completed. This class guarantees that only a single popup is visible at any one time. Other requests are queued and displayed after the user dismisses the current popup. :param callback: A callable that is called with payload if the user asks to proceed. Note that this is always called in the GUI thread. :param cancel_callback: A callable that is called with the payload if the users asks not to proceed. :param payload: Arbitrary object, passed to callback :param html_log: An HTML or plain text log :param log_viewer_title: The title for the log viewer window :param title: The title for this popup :param msg: The msg to display :param det_msg: Detailed message :param log_is_file: If True the html_log parameter is interpreted as the path to a file on disk containing the log encoded with utf-8 :param checkbox_msg: If not None, a checkbox is displayed in the dialog, showing this message. The callback is called with both the payload and the state of the checkbox as arguments. :param checkbox_checked: If True the checkbox is checked by default. :param action_callback: If not None, an extra button is added, which when clicked will cause action_callback to be called instead of callback. action_callback is called in exactly the same way as callback. :param action_label: The text on the action button :param action_icon: The icon for the action button, must be a QIcon object or None :param focus_action: If True, the action button will be focused instead of the Yes button :param show_det: If True, the Detailed message will be shown initially :param show_ok: If True, OK will be shown instead of YES/NO :param geom_pref: String for preference name to preserve dialog box geometry ''' question = Question( payload, callback, cancel_callback, title, msg, html_log, log_viewer_title, log_is_file, det_msg, show_copy_button, checkbox_msg, checkbox_checked, action_callback, action_label, action_icon, focus_action, show_det, show_ok, geom_pref) self.questions.append(question) self.show_question() def show_log(self): if self.questions: q = self.questions[0] log = q.html_log if q.log_is_file: with open(log, 'rb') as f: log = f.read().decode('utf-8') self.log_viewer = ViewLog(q.log_viewer_title, log, parent=self)
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() # Login on the account login(self) # Create the title font self.titleFont = QFont() self.titleFont.setBold(True) self.titleFont.setPixelSize(24) # Create the window self.width = 750 self.height = 475 self.setGeometry(10, 10, self.width, self.height) self.setFixedSize(self.width, self.height) self.initUI() self.show() def initUI(self): # Title title = QLabel(self) title.setFont(self.titleFont) title.setText("TEXT TO THREAD") title.setMinimumWidth(int(self.width / 2)) title.move(5, 22) # Text box self.text = QTextEdit(self) self.text.setMinimumWidth(self.width - 10) self.text.setMinimumHeight(self.height - 110) self.text.move(5, 50) # Publish button self.send = QPushButton(self) self.send.setText("Publish") self.send.setMinimumWidth(self.width - 10) self.send.move(5, self.height - 35) self.send.clicked.connect(self.convertToThread) # Add images checkbox self.images = QCheckBox(self) self.images.setText("Add images") self.images.move(10, self.height - 62) # Create the menu bar menu = self.menuBar() actions = menu.addMenu("&Actions") # Clear the text box new = QAction("&New", self) new.setShortcut("Ctrl+N") new.setStatusTip("Clear the window") new.triggered.connect(self.text.clear) actions.addAction(new) # Send the tweets send = QAction("&Send", self) send.setShortcut("Ctrl+Return") send.setStatusTip("Send the tweets") send.triggered.connect(self.convertToThread) actions.addAction(send) # Rewrite the access credentials credentials = QAction("&Change credentials", self) credentials.setShortcut("Ctrl+R") credentials.setStatusTip("Change the credentials, restart required") credentials.triggered.connect(self.changeKeys) actions.addAction(credentials) # Exit from the app exit = QAction("&Exit", self) exit.setShortcut("Ctrl+Q") exit.setStatusTip("Exit from the app") exit.triggered.connect(sys.exit) actions.addAction(exit) def changeKeys(self): # Ask if user wants to continue # If true, change credentials and close the app yesNo = QMessageBox.question(self, "", "Restart required, continue?", QMessageBox.Yes | QMessageBox.No) if yesNo == QMessageBox.Yes: changeKeys(self) sys.exit() def convertToThread(self): # Get the text from the input text = self.text.toPlainText() # Create a list and fill it with the paragraphs listPar = [] initChar = 0 i = 0 for char in text: # Check if change of line, if true, add the paragraph to the list if char == "\n": listPar.append(text[initChar:i]) initChar = i + 1 # Check if end of the text, if true, add the paragraph to the list if i == len(text) - 1: listPar.append(text[initChar:i + 1]) i += 1 # Dividing the text into 280 or less characters strings self.listTweets = [] listDots = [] for par in listPar: # Remove from the list the paragraph without text if len(par) <= 280: self.listTweets.append(par) else: # Select the points where are "." listDots = [] initChar = 0 for i in range(0, len(par)): if par[i] == ".": listDots.append(i) # Pass in the dots list for i in range(0, len(listDots)): # Check if the dot position is more than 280 # If true add it to the tweets list if listDots[i] - initChar > 280: self.listTweets.append(par[initChar:listDots[i - 1] + 1]) initChar = listDots[i - 1] + 2 # Check is the dot is the last one in the list # If true, add it to the list if i == len(listDots) - 1: self.listTweets.append(par[initChar:listDots[i] + 1]) # Check if checkbox is checked, if true, execute add images function # If not, convert tweets format and publish them if self.images.isChecked(): self.addImages() else: self.tweets = [] for tweet in self.listTweets: self.tweets.append({"tweet": tweet, "image": ""}) self.publish() def addImages(self): self.tweets = [] for tweet in self.listTweets: # Ignore empty tweets if tweet != "": # Ask if add add image message = 'Add an image to the tweet: "' + tweet + '"?' yesNo = QMessageBox.question(self, "Do you want to add an image?", message, QMessageBox.Yes | QMessageBox.No) # Ask the link of the image if yesNo == QMessageBox.Yes: url, ok = QInputDialog.getText(self, "Image URL", "Image URL: ", QLineEdit.Normal, "") self.tweets.append({"tweet": tweet, "image": url}) else: self.tweets.append({"tweet": tweet, "image": ""}) self.publish() def publish(self): i = 0 # Pass into the tweets for tweet in self.tweets: # Ignore empty tweets if tweet["tweet"] != "": i += 1 # Check if the tweet has an image if tweet["image"] == "": # Check if the tweet is a response or the first and send it if i == 1: status = api.update_status(tweet["tweet"]) else: status = api.update_status(tweet["tweet"], in_reply_to_status_id=ID) else: # Download the image file = filename(tweet["image"]) # Check if the tweet is a response or the first and send it if i == 1: status = api.update_with_media(file, status=tweet["tweet"]) else: status = api.update_with_media( file, status=tweet["tweet"], in_reply_to_status_id=ID) os.remove(file) ID = status.id
class ProceedQuestion(QDialog): ask_question = pyqtSignal(object, object, object) def __init__(self, parent): QDialog.__init__(self, parent) self.setAttribute(Qt.WA_DeleteOnClose, False) self.setWindowIcon(QIcon(I('dialog_question.png'))) self.questions = [] self._l = l = QGridLayout(self) self.setLayout(l) self.icon_label = ic = QLabel(self) ic.setPixmap(QPixmap(I('dialog_question.png'))) self.msg_label = msg = QLabel('some random filler text') msg.setWordWrap(True) ic.setMaximumWidth(110) ic.setMaximumHeight(100) ic.setScaledContents(True) ic.setStyleSheet('QLabel { margin-right: 10px }') self.bb = QDialogButtonBox() self.bb.accepted.connect(self.accept) self.bb.rejected.connect(self.reject) self.log_button = self.bb.addButton(_('View log'), self.bb.ActionRole) self.log_button.setIcon(QIcon(I('debug.png'))) self.log_button.clicked.connect(self.show_log) self.copy_button = self.bb.addButton(_('&Copy to clipboard'), self.bb.ActionRole) self.copy_button.clicked.connect(self.copy_to_clipboard) self.action_button = self.bb.addButton('', self.bb.ActionRole) self.action_button.clicked.connect(self.action_clicked) self.show_det_msg = _('Show &details') self.hide_det_msg = _('Hide &details') self.det_msg_toggle = self.bb.addButton(self.show_det_msg, self.bb.ActionRole) self.det_msg_toggle.clicked.connect(self.toggle_det_msg) self.det_msg_toggle.setToolTip( _('Show detailed information about this error')) self.det_msg = QPlainTextEdit(self) self.det_msg.setReadOnly(True) self.bb.setStandardButtons(self.bb.Yes | self.bb.No | self.bb.Ok) self.bb.button(self.bb.Yes).setDefault(True) self.checkbox = QCheckBox('', self) l.addWidget(ic, 0, 0, 1, 1) l.addWidget(msg, 0, 1, 1, 1) l.addWidget(self.checkbox, 1, 0, 1, 2) l.addWidget(self.det_msg, 2, 0, 1, 2) l.addWidget(self.bb, 3, 0, 1, 2) self.ask_question.connect(self.do_ask_question, type=Qt.QueuedConnection) def copy_to_clipboard(self, *args): QApplication.clipboard().setText( 'calibre, version %s\n%s: %s\n\n%s' % (__version__, unicode(self.windowTitle()), unicode(self.msg_label.text()), unicode( self.det_msg.toPlainText()))) self.copy_button.setText(_('Copied')) def action_clicked(self): if self.questions: q = self.questions[0] self.questions[0] = q._replace(callback=q.action_callback) self.accept() def accept(self): if self.geom_pref: gprefs[self.geom_pref] = bytearray(self.saveGeometry()) if self.questions: payload, callback, cancel_callback = self.questions[0][:3] self.questions = self.questions[1:] cb = None if self.checkbox.isVisible(): cb = bool(self.checkbox.isChecked()) self.ask_question.emit(callback, payload, cb) self.hide() def reject(self): if self.geom_pref: gprefs[self.geom_pref] = bytearray(self.saveGeometry()) if self.questions: payload, callback, cancel_callback = self.questions[0][:3] self.questions = self.questions[1:] cb = None if self.checkbox.isVisible(): cb = bool(self.checkbox.isChecked()) self.ask_question.emit(cancel_callback, payload, cb) self.hide() def do_ask_question(self, callback, payload, checkbox_checked): if callable(callback): args = [payload] if checkbox_checked is not None: args.append(checkbox_checked) callback(*args) self.show_question() def toggle_det_msg(self, *args): vis = unicode(self.det_msg_toggle.text()) == self.hide_det_msg self.det_msg_toggle.setText( self.show_det_msg if vis else self.hide_det_msg) self.det_msg.setVisible(not vis) self.do_resize() def do_resize(self): if self.geom_pref: geom = gprefs.get(self.geom_pref, None) if geom: self.restoreGeometry(geom) return sz = self.sizeHint() + QSize(100, 0) sz.setWidth(min(500, sz.width())) sz.setHeight(min(500, sz.height())) self.resize(sz) def show_question(self): if self.isVisible(): return if self.questions: question = self.questions[0] self.msg_label.setText(question.msg) self.setWindowTitle(question.title) self.log_button.setVisible(bool(question.html_log)) self.copy_button.setText(_('&Copy to clipboard')) self.copy_button.setVisible(bool(question.show_copy_button)) self.action_button.setVisible(question.action_callback is not None) if question.action_callback is not None: self.action_button.setText(question.action_label or '') self.action_button.setIcon(QIcon( ) if question.action_icon is None else question.action_icon) self.det_msg.setPlainText(question.det_msg or '') self.det_msg.setVisible(False) self.det_msg_toggle.setVisible(bool(question.det_msg)) self.det_msg_toggle.setText(self.show_det_msg) self.checkbox.setVisible(question.checkbox_msg is not None) if question.checkbox_msg is not None: self.checkbox.setText(question.checkbox_msg) self.checkbox.setChecked(question.checkbox_checked) self.bb.button(self.bb.Ok).setVisible(question.show_ok) self.bb.button(self.bb.Yes).setVisible(not question.show_ok) self.bb.button(self.bb.No).setVisible(not question.show_ok) self.geom_pref = ( 'proceed question dialog:' + question.geom_pref) if question.geom_pref else None if question.show_det: self.toggle_det_msg() else: self.do_resize() self.show() button = self.action_button if question.focus_action and question.action_callback is not None else \ (self.bb.button(self.bb.Ok) if question.show_ok else self.bb.button(self.bb.Yes)) button.setDefault(True) button.setFocus(Qt.OtherFocusReason) def __call__(self, callback, payload, html_log, log_viewer_title, title, msg, det_msg='', show_copy_button=False, cancel_callback=None, log_is_file=False, checkbox_msg=None, checkbox_checked=False, action_callback=None, action_label=None, action_icon=None, focus_action=False, show_det=False, show_ok=False, geom_pref=None): ''' A non modal popup that notifies the user that a background task has been completed. This class guarantees that only a single popup is visible at any one time. Other requests are queued and displayed after the user dismisses the current popup. :param callback: A callable that is called with payload if the user asks to proceed. Note that this is always called in the GUI thread. :param cancel_callback: A callable that is called with the payload if the users asks not to proceed. :param payload: Arbitrary object, passed to callback :param html_log: An HTML or plain text log :param log_viewer_title: The title for the log viewer window :param title: The title for this popup :param msg: The msg to display :param det_msg: Detailed message :param log_is_file: If True the html_log parameter is interpreted as the path to a file on disk containing the log encoded with utf-8 :param checkbox_msg: If not None, a checkbox is displayed in the dialog, showing this message. The callback is called with both the payload and the state of the checkbox as arguments. :param checkbox_checked: If True the checkbox is checked by default. :param action_callback: If not None, an extra button is added, which when clicked will cause action_callback to be called instead of callback. action_callback is called in exactly the same way as callback. :param action_label: The text on the action button :param action_icon: The icon for the action button, must be a QIcon object or None :param focus_action: If True, the action button will be focused instead of the Yes button :param show_det: If True, the Detailed message will be shown initially :param show_ok: If True, OK will be shown instead of YES/NO :param geom_pref: String for preference name to preserve dialog box geometry ''' question = Question(payload, callback, cancel_callback, title, msg, html_log, log_viewer_title, log_is_file, det_msg, show_copy_button, checkbox_msg, checkbox_checked, action_callback, action_label, action_icon, focus_action, show_det, show_ok, geom_pref) self.questions.append(question) self.show_question() def show_log(self): if self.questions: q = self.questions[0] log = q.html_log if q.log_is_file: with open(log, 'rb') as f: log = f.read().decode('utf-8') self.log_viewer = ViewLog(q.log_viewer_title, log, parent=self)
class ProceedQuestion(QWidget): ask_question = pyqtSignal(object, object, object) @pyqtProperty(float) def show_fraction(self): return self._show_fraction @show_fraction.setter def show_fraction(self, val): self._show_fraction = max(0, min(1, float(val))) self.update() def __init__(self, parent): QWidget.__init__(self, parent) self.setVisible(False) parent.installEventFilter(self) self._show_fraction = 0.0 self.show_animation = a = QPropertyAnimation(self, b"show_fraction", self) a.setDuration(1000), a.setEasingCurve(QEasingCurve.OutQuad) a.setStartValue(0.0), a.setEndValue(1.0) a.finished.connect(self.stop_show_animation) self.rendered_pixmap = None self.questions = [] self.icon = ic = Icon(self) self.msg_label = msg = QLabel('some random filler text') msg.setWordWrap(True) self.bb = QDialogButtonBox() self.bb.accepted.connect(self.accept) self.bb.rejected.connect(self.reject) self.log_button = self.bb.addButton(_('View log'), self.bb.ActionRole) self.log_button.setIcon(QIcon(I('debug.png'))) self.log_button.clicked.connect(self.show_log) self.copy_button = self.bb.addButton(_('&Copy to clipboard'), self.bb.ActionRole) self.copy_button.clicked.connect(self.copy_to_clipboard) self.action_button = self.bb.addButton('', self.bb.ActionRole) self.action_button.clicked.connect(self.action_clicked) self.show_det_msg = _('Show &details') self.hide_det_msg = _('Hide &details') self.det_msg_toggle = self.bb.addButton(self.show_det_msg, self.bb.ActionRole) self.det_msg_toggle.clicked.connect(self.toggle_det_msg) self.det_msg_toggle.setToolTip( _('Show detailed information about this error')) self.det_msg = PlainTextEdit(self) self.det_msg.setReadOnly(True) self.bb.setStandardButtons(self.bb.Yes|self.bb.No|self.bb.Ok) self.bb.button(self.bb.Yes).setDefault(True) self.title_label = title = QLabel('A dummy title') f = title.font() f.setBold(True) title.setFont(f) self.checkbox = QCheckBox('', self) self._l = l = QVBoxLayout(self) self._h = h = QHBoxLayout() self._v = v = QVBoxLayout() v.addWidget(title), v.addWidget(msg) h.addWidget(ic), h.addSpacing(10), h.addLayout(v), l.addLayout(h) l.addSpacing(5) l.addWidget(self.checkbox) l.addWidget(self.det_msg) l.addWidget(self.bb) self.ask_question.connect(self.do_ask_question, type=Qt.QueuedConnection) self.setFocusPolicy(Qt.NoFocus) for child in self.findChildren(QWidget): child.setFocusPolicy(Qt.NoFocus) self.setFocusProxy(self.parent()) self.resize_timer = t = QTimer(self) t.setSingleShot(True), t.setInterval(100), t.timeout.connect(self.parent_resized) def eventFilter(self, obj, ev): if ev.type() == ev.Resize and self.isVisible(): self.resize_timer.start() return False def parent_resized(self): if self.isVisible(): self.do_resize() def copy_to_clipboard(self, *args): QApplication.clipboard().setText( 'calibre, version %s\n%s: %s\n\n%s' % (__version__, unicode_type(self.windowTitle()), unicode_type(self.msg_label.text()), unicode_type(self.det_msg.toPlainText()))) self.copy_button.setText(_('Copied')) def action_clicked(self): if self.questions: q = self.questions[0] self.questions[0] = q._replace(callback=q.action_callback) self.accept() def accept(self): if self.questions: payload, callback, cancel_callback = self.questions[0][:3] self.questions = self.questions[1:] cb = None if self.checkbox.isVisible(): cb = bool(self.checkbox.isChecked()) self.ask_question.emit(callback, payload, cb) self.hide() def reject(self): if self.questions: payload, callback, cancel_callback = self.questions[0][:3] self.questions = self.questions[1:] cb = None if self.checkbox.isVisible(): cb = bool(self.checkbox.isChecked()) self.ask_question.emit(cancel_callback, payload, cb) self.hide() def do_ask_question(self, callback, payload, checkbox_checked): if callable(callback): args = [payload] if checkbox_checked is not None: args.append(checkbox_checked) callback(*args) self.show_question() def toggle_det_msg(self, *args): vis = unicode_type(self.det_msg_toggle.text()) == self.hide_det_msg self.det_msg_toggle.setText(self.show_det_msg if vis else self.hide_det_msg) self.det_msg.setVisible(not vis) self.do_resize() def do_resize(self): sz = self.sizeHint() sz.setWidth(min(self.parent().width(), sz.width())) sb = self.parent().statusBar().height() + 10 sz.setHeight(min(self.parent().height() - sb, sz.height())) self.resize(sz) self.position_widget() def show_question(self): if not self.questions: return if not self.isVisible(): question = self.questions[0] self.msg_label.setText(question.msg) self.icon.set_icon(question.icon) self.title_label.setText(question.title) self.log_button.setVisible(bool(question.html_log)) self.copy_button.setText(_('&Copy to clipboard')) if question.action_callback is not None: self.action_button.setText(question.action_label or '') self.action_button.setIcon( QIcon() if question.action_icon is None else question.action_icon) # Force the button box to relayout its buttons, as button text # might have changed self.bb.setOrientation(Qt.Vertical), self.bb.setOrientation(Qt.Horizontal) self.det_msg.setPlainText(question.det_msg or '') self.det_msg.setVisible(False) self.det_msg_toggle.setVisible(bool(question.det_msg)) self.det_msg_toggle.setText(self.show_det_msg) self.checkbox.setVisible(question.checkbox_msg is not None) if question.checkbox_msg is not None: self.checkbox.setText(question.checkbox_msg) self.checkbox.setChecked(question.checkbox_checked) self.bb.button(self.bb.Ok).setVisible(question.show_ok) self.bb.button(self.bb.Yes).setVisible(not question.show_ok) self.bb.button(self.bb.No).setVisible(not question.show_ok) self.copy_button.setVisible(bool(question.show_copy_button)) self.action_button.setVisible(question.action_callback is not None) self.toggle_det_msg() if question.show_det else self.do_resize() self.show_widget() button = self.action_button if question.focus_action and question.action_callback is not None else \ (self.bb.button(self.bb.Ok) if question.show_ok else self.bb.button(self.bb.Yes)) button.setDefault(True) self.raise_() self.start_show_animation() def start_show_animation(self): if self.rendered_pixmap is not None: return dpr = getattr(self, 'devicePixelRatioF', self.devicePixelRatio)() p = QImage(dpr * self.size(), QImage.Format_ARGB32_Premultiplied) p.setDevicePixelRatio(dpr) self.render(p) self.rendered_pixmap = QPixmap.fromImage(p) self.original_visibility = v = [] for child in self.findChildren(QWidget): if child.isVisible(): child.setVisible(False) v.append(child) self.show_animation.start() def stop_show_animation(self): self.rendered_pixmap = None [c.setVisible(True) for c in getattr(self, 'original_visibility', ())] self.update() for child in self.findChildren(QWidget): child.update() if hasattr(child, 'viewport'): child.viewport().update() def position_widget(self): geom = self.parent().geometry() x = geom.width() - self.width() - 5 sb = self.parent().statusBar() if sb is None: y = geom.height() - self.height() else: y = sb.geometry().top() - self.height() self.move(x, y) def show_widget(self): self.show() self.position_widget() def dummy_question(self, action_label=None): self(lambda *args:args, (), 'dummy log', 'Log Viewer', 'A Dummy Popup', 'This is a dummy popup to easily test things, with a long line of text that should wrap. ' 'This is a dummy popup to easily test things, with a long line of text that should wrap', checkbox_msg='A dummy checkbox', action_callback=lambda *args: args, action_label=action_label or 'An action') def __call__(self, callback, payload, html_log, log_viewer_title, title, msg, det_msg='', show_copy_button=False, cancel_callback=None, log_is_file=False, checkbox_msg=None, checkbox_checked=False, action_callback=None, action_label=None, action_icon=None, focus_action=False, show_det=False, show_ok=False, icon=None, log_viewer_unique_name=None, **kw): ''' A non modal popup that notifies the user that a background task has been completed. This class guarantees that only a single popup is visible at any one time. Other requests are queued and displayed after the user dismisses the current popup. :param callback: A callable that is called with payload if the user asks to proceed. Note that this is always called in the GUI thread. :param cancel_callback: A callable that is called with the payload if the users asks not to proceed. :param payload: Arbitrary object, passed to callback :param html_log: An HTML or plain text log :param log_viewer_title: The title for the log viewer window :param title: The title for this popup :param msg: The msg to display :param det_msg: Detailed message :param log_is_file: If True the html_log parameter is interpreted as the path to a file on disk containing the log encoded with utf-8 :param checkbox_msg: If not None, a checkbox is displayed in the dialog, showing this message. The callback is called with both the payload and the state of the checkbox as arguments. :param checkbox_checked: If True the checkbox is checked by default. :param action_callback: If not None, an extra button is added, which when clicked will cause action_callback to be called instead of callback. action_callback is called in exactly the same way as callback. :param action_label: The text on the action button :param action_icon: The icon for the action button, must be a QIcon object or None :param focus_action: If True, the action button will be focused instead of the Yes button :param show_det: If True, the Detailed message will be shown initially :param show_ok: If True, OK will be shown instead of YES/NO :param icon: The icon to be used for this popop (defaults to question mark). Can be either a QIcon or a string to be used with I() :log_viewer_unique_name: If set, ViewLog will remember/reuse its size for this name in calibre.gui2.gprefs ''' question = Question( payload, callback, cancel_callback, title, msg, html_log, log_viewer_title, log_is_file, det_msg, show_copy_button, checkbox_msg, checkbox_checked, action_callback, action_label, action_icon, focus_action, show_det, show_ok, icon, log_viewer_unique_name) self.questions.append(question) self.show_question() def show_log(self): if self.questions: q = self.questions[0] log = q.html_log if q.log_is_file: with open(log, 'rb') as f: log = f.read().decode('utf-8') self.log_viewer = ViewLog(q.log_viewer_title, log, parent=self, unique_name=q.log_viewer_unique_name) def paintEvent(self, ev): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.SmoothPixmapTransform, True) try: if self.rendered_pixmap is None: self.paint_background(painter) else: self.animated_paint(painter) finally: painter.end() def animated_paint(self, painter): top = (1 - self._show_fraction) * self.height() painter.drawPixmap(0, top, self.rendered_pixmap) def paint_background(self, painter): br = 12 # border_radius bw = 1 # border_width pal = self.palette() c = pal.color(pal.Window) c.setAlphaF(0.9) p = QPainterPath() p.addRoundedRect(QRectF(self.rect()), br, br) painter.fillPath(p, c) p.addRoundedRect(QRectF(self.rect()).adjusted(bw, bw, -bw, -bw), br, br) painter.fillPath(p, pal.color(pal.WindowText))
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()
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)