%PDF- %PDF-
Direktori : /proc/thread-self/root/usr/lib/calibre/calibre/gui2/tweak_book/editor/ |
Current File : //proc/thread-self/root/usr/lib/calibre/calibre/gui2/tweak_book/editor/image.py |
#!/usr/bin/env python3 __license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>' import sys from qt.core import ( QMainWindow, Qt, QApplication, pyqtSignal, QLabel, QIcon, QFormLayout, QSize, QDialog, QSpinBox, QCheckBox, QDialogButtonBox, QToolButton, QMenu, QInputDialog) from calibre.gui2 import error_dialog from calibre.gui2.tweak_book import actions, tprefs, editors from calibre.gui2.tweak_book.editor.canvas import Canvas from polyglot.builtins import itervalues class ResizeDialog(QDialog): # {{{ def __init__(self, width, height, parent=None): QDialog.__init__(self, parent) self.l = l = QFormLayout(self) self.setLayout(l) self.aspect_ratio = width / float(height) l.addRow(QLabel(_('Choose the new width and height'))) self._width = w = QSpinBox(self) w.setMinimum(1) w.setMaximum(10 * width) w.setValue(width) w.setSuffix(' px') l.addRow(_('&Width:'), w) self._height = h = QSpinBox(self) h.setMinimum(1) h.setMaximum(10 * height) h.setValue(height) h.setSuffix(' px') l.addRow(_('&Height:'), h) connect_lambda(w.valueChanged, self, lambda self: self.keep_ar('width')) connect_lambda(h.valueChanged, self, lambda self: self.keep_ar('height')) self.ar = ar = QCheckBox(_('Keep &aspect ratio')) ar.setChecked(True) l.addRow(ar) self.resize(self.sizeHint()) self.bb = bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) l.addRow(bb) def keep_ar(self, which): if self.ar.isChecked(): val = getattr(self, which) oval = val / self.aspect_ratio if which == 'width' else val * self.aspect_ratio other = getattr(self, '_height' if which == 'width' else '_width') other.blockSignals(True) other.setValue(int(oval)) other.blockSignals(False) @property def width(self): return self._width.value() @width.setter def width(self, val): self._width.setValue(val) @property def height(self): return self._height.value() @height.setter def height(self, val): self._height.setValue(val) # }}} class Editor(QMainWindow): has_line_numbers = False modification_state_changed = pyqtSignal(object) undo_redo_state_changed = pyqtSignal(object, object) data_changed = pyqtSignal(object) cursor_position_changed = pyqtSignal() # dummy copy_available_state_changed = pyqtSignal(object) def __init__(self, syntax, parent=None): QMainWindow.__init__(self, parent) if parent is None: self.setWindowFlags(Qt.WindowType.Widget) self.is_synced_to_container = False self.syntax = syntax self._is_modified = False self.copy_available = self.cut_available = False self.quality = 90 self.canvas = Canvas(self) self.setCentralWidget(self.canvas) self.create_toolbars() self.canvas.image_changed.connect(self.image_changed) self.canvas.undo_redo_state_changed.connect(self.undo_redo_state_changed) self.canvas.selection_state_changed.connect(self.update_clipboard_actions) @property def is_modified(self): return self._is_modified @is_modified.setter def is_modified(self, val): self._is_modified = val self.modification_state_changed.emit(val) @property def current_editing_state(self): return {} @current_editing_state.setter def current_editing_state(self, val): pass @property def undo_available(self): return self.canvas.undo_action.isEnabled() @property def redo_available(self): return self.canvas.redo_action.isEnabled() @property def current_line(self): return 0 @current_line.setter def current_line(self, val): pass @property def number_of_lines(self): return 0 def pretty_print(self, name): return False def change_document_name(self, newname): pass def get_raw_data(self): return self.canvas.get_image_data(quality=self.quality) @property def data(self): return self.get_raw_data() @data.setter def data(self, val): self.canvas.load_image(val) self._is_modified = False # The image_changed signal will have been triggered causing this editor to be incorrectly marked as modified def replace_data(self, raw, only_if_different=True): # We ignore only_if_different as it is useless in our case, and # there is no easy way to check two images for equality self.data = raw def apply_settings(self, prefs=None, dictionaries_changed=False): pass def go_to_line(self, *args, **kwargs): pass def save_state(self): for bar in self.bars: if bar.isFloating(): return tprefs['image-editor-state'] = bytearray(self.saveState()) def restore_state(self): state = tprefs.get('image-editor-state', None) if state is not None: self.restoreState(state) def set_focus(self): self.canvas.setFocus(Qt.FocusReason.OtherFocusReason) def undo(self): self.canvas.undo_action.trigger() def redo(self): self.canvas.redo_action.trigger() def copy(self): self.canvas.copy() def cut(self): return error_dialog(self, _('Not allowed'), _( 'Cutting of images is not allowed. If you want to delete the image, use' ' the files browser to do it.'), show=True) def paste(self): self.canvas.paste() # Search and replace {{{ def mark_selected_text(self, *args, **kwargs): pass def find(self, *args, **kwargs): return False def replace(self, *args, **kwargs): return False def all_in_marked(self, *args, **kwargs): return 0 @property def selected_text(self): return '' # }}} def image_changed(self, new_image): self.is_synced_to_container = False self._is_modified = True self.copy_available = self.canvas.is_valid self.copy_available_state_changed.emit(self.copy_available) self.data_changed.emit(self) self.modification_state_changed.emit(True) self.fmt_label.setText(' ' + (self.canvas.original_image_format or '').upper()) im = self.canvas.current_image self.size_label.setText('{} x {}{}'.format(im.width(), im.height(), ' px')) def break_cycles(self): self.canvas.break_cycles() self.canvas.image_changed.disconnect() self.canvas.undo_redo_state_changed.disconnect() self.canvas.selection_state_changed.disconnect() self.modification_state_changed.disconnect() self.undo_redo_state_changed.disconnect() self.data_changed.disconnect() self.cursor_position_changed.disconnect() self.copy_available_state_changed.disconnect() def contextMenuEvent(self, ev): ev.ignore() def create_toolbars(self): self.action_bar = b = self.addToolBar(_('File actions tool bar')) b.setObjectName('action_bar') # Needed for saveState for x in ('undo', 'redo'): b.addAction(getattr(self.canvas, '%s_action' % x)) self.edit_bar = b = self.addToolBar(_('Edit actions tool bar')) b.setObjectName('edit-actions-bar') for x in ('copy', 'paste'): ac = actions['editor-%s' % x] setattr(self, 'action_' + x, b.addAction(ac.icon(), x, getattr(self, x))) self.update_clipboard_actions() b.addSeparator() self.action_trim = ac = b.addAction(QIcon(I('trim.png')), _('Trim image'), self.canvas.trim_image) self.action_rotate = ac = b.addAction(QIcon(I('rotate-right.png')), _('Rotate image'), self.canvas.rotate_image) self.action_resize = ac = b.addAction(QIcon(I('resize.png')), _('Resize image'), self.resize_image) b.addSeparator() self.action_filters = ac = b.addAction(QIcon(I('filter.png')), _('Image filters')) b.widgetForAction(ac).setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup) self.filters_menu = m = QMenu(self) ac.setMenu(m) m.addAction(_('Auto-trim image'), self.canvas.autotrim_image) m.addAction(_('Sharpen image'), self.sharpen_image) m.addAction(_('Blur image'), self.blur_image) m.addAction(_('De-speckle image'), self.canvas.despeckle_image) m.addAction(_('Improve contrast (normalize image)'), self.canvas.normalize_image) m.addAction(_('Make image look like an oil painting'), self.oilify_image) self.info_bar = b = self.addToolBar(_('Image information bar')) b.setObjectName('image_info_bar') self.fmt_label = QLabel('') b.addWidget(self.fmt_label) b.addSeparator() self.size_label = QLabel('') b.addWidget(self.size_label) self.bars = [self.action_bar, self.edit_bar, self.info_bar] for x in self.bars: x.setFloatable(False) x.topLevelChanged.connect(self.toolbar_floated) x.setIconSize(QSize(tprefs['toolbar_icon_size'], tprefs['toolbar_icon_size'])) self.restore_state() def toolbar_floated(self, floating): if not floating: self.save_state() for ed in itervalues(editors): if ed is not self: ed.restore_state() def update_clipboard_actions(self, *args): if self.canvas.has_selection: self.action_copy.setText(_('Copy selected region')) self.action_paste.setText(_('Paste into selected region')) else: self.action_copy.setText(_('Copy image')) self.action_paste.setText(_('Paste image')) def resize_image(self): im = self.canvas.current_image d = ResizeDialog(im.width(), im.height(), self) if d.exec() == QDialog.DialogCode.Accepted: self.canvas.resize_image(d.width, d.height) def sharpen_image(self): val, ok = QInputDialog.getInt(self, _('Sharpen image'), _( 'The standard deviation for the Gaussian sharpen operation (higher means more sharpening)'), value=3, min=1, max=20) if ok: self.canvas.sharpen_image(sigma=val) def blur_image(self): val, ok = QInputDialog.getInt(self, _('Blur image'), _( 'The standard deviation for the Gaussian blur operation (higher means more blurring)'), value=3, min=1, max=20) if ok: self.canvas.blur_image(sigma=val) def oilify_image(self): val, ok = QInputDialog.getDouble(self, _('Oilify image'), _( 'The strength of the operation (higher numbers have larger effects)'), value=4, min=0.1, max=20) if ok: self.canvas.oilify_image(radius=val) def launch_editor(path_to_edit, path_is_raw=False): app = QApplication([]) if path_is_raw: raw = path_to_edit else: with open(path_to_edit, 'rb') as f: raw = f.read() t = Editor('raster_image') t.data = raw t.show() app.exec() if __name__ == '__main__': launch_editor(sys.argv[-1])