diff --git a/imgsrc/resize.svg b/imgsrc/resize.svg new file mode 100644 index 0000000000..1d33cd86c5 --- /dev/null +++ b/imgsrc/resize.svg @@ -0,0 +1,5299 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Oxygen team + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/resize.png b/resources/images/resize.png new file mode 100644 index 0000000000..f2ae6afa65 Binary files /dev/null and b/resources/images/resize.png differ diff --git a/src/calibre/gui2/tweak_book/editor/canvas.py b/src/calibre/gui2/tweak_book/editor/canvas.py index de1d974e76..435920782f 100644 --- a/src/calibre/gui2/tweak_book/editor/canvas.py +++ b/src/calibre/gui2/tweak_book/editor/canvas.py @@ -10,7 +10,7 @@ import sys, string, weakref from functools import wraps from PyQt4.Qt import ( - QWidget, QPainter, QColor, QApplication, Qt, QPixmap, QRectF, + QWidget, QPainter, QColor, QApplication, Qt, QPixmap, QRectF, QMatrix, QPointF, QPen, pyqtSignal, QUndoCommand, QUndoStack, QIcon, QImage) from calibre import fit_image @@ -89,6 +89,27 @@ class Trim(Command): sr = canvas.selection_state.rect return img.copy(*get_selection_rect(img, sr, target)) +class Rotate(Command): + + def __init__(self, canvas): + Command.__init__(self, _('Rotate image'), canvas) + + def __call__(self, canvas): + img = canvas.current_image + m = QMatrix() + m.rotate(90) + return img.transformed(m, Qt.SmoothTransformation) + +class Scale(Command): + + def __init__(self, width, height, canvas): + self.width, self.height = width, height + Command.__init__(self, _('Resize image'), canvas) + + def __call__(self, canvas): + img = canvas.current_image + return img.scaled(self.width, self.height, transformMode=Qt.SmoothTransformation) + class Replace(Command): ''' Replace the current image with another image. If there is a selection, @@ -226,6 +247,16 @@ class Canvas(QWidget): self.undo_stack.push(Trim(self)) return True + @imageop + def rotate_image(self): + self.undo_stack.push(Rotate(self)) + return True + + @imageop + def resize_image(self, width, height): + self.undo_stack.push(Scale(width, height, self)) + return True + # The selection rectangle {{{ @property def dc_size(self): @@ -378,7 +409,6 @@ class Canvas(QWidget): elif self.selection_state.current_mode == 'selected' and self.selection_state.rect is not None and self.selection_state.rect.contains(ev.pos()): self.setCursor(self.get_cursor()) self.update() - # }}} def keyPressEvent(self, ev): k = ev.key() @@ -394,6 +424,7 @@ class Canvas(QWidget): self.update() else: return QWidget.keyPressEvent(self, ev) + # }}} # Painting {{{ @painter diff --git a/src/calibre/gui2/tweak_book/editor/image.py b/src/calibre/gui2/tweak_book/editor/image.py index 656f59bd45..c35143851b 100644 --- a/src/calibre/gui2/tweak_book/editor/image.py +++ b/src/calibre/gui2/tweak_book/editor/image.py @@ -7,14 +7,77 @@ __license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal ' import sys +from functools import partial from PyQt4.Qt import ( - QMainWindow, Qt, QApplication, pyqtSignal) + QMainWindow, Qt, QApplication, pyqtSignal, QLabel, QIcon, QFormLayout, + QDialog, QSpinBox, QCheckBox, QDialogButtonBox) from calibre.gui2 import error_dialog from calibre.gui2.tweak_book import actions from calibre.gui2.tweak_book.editor.canvas import Canvas +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) + w.valueChanged.connect(partial(self.keep_ar, 'width')) + h.valueChanged.connect(partial(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.Ok | QDialogButtonBox.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(oval) + other.blockSignals(False) + + @dynamic_property + def width(self): + def fget(self): + return self._width.value() + def fset(self, val): + self._width.setValue(val) + return property(fget=fget, fset=fset) + + @dynamic_property + def height(self): + def fget(self): + return self._height.value() + def fset(self, val): + self._height.setValue(val) + return property(fget=fget, fset=fset) +# }}} + class Editor(QMainWindow): has_line_numbers = False @@ -137,6 +200,9 @@ class Editor(QMainWindow): 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('{0} x {1}{2}'.format(im.width(), im.height(), 'px')) def break_cycles(self): self.canvas.break_cycles() @@ -168,6 +234,18 @@ class Editor(QMainWindow): 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) + + self.info_bar = b = self.addToolBar(_('Image information bar')) + self.fmt_label = QLabel('') + b.addWidget(self.fmt_label) + b.addSeparator() + self.size_label = QLabel('') + b.addWidget(self.size_label) + def update_clipboard_actions(self, *args): if self.canvas.has_selection: self.action_copy.setText(_('Copy selected region')) @@ -176,6 +254,12 @@ class Editor(QMainWindow): 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_() == d.Accepted: + self.canvas.resize_image(d.width, d.height) + def launch_editor(path_to_edit, path_is_raw=False): app = QApplication([]) if path_is_raw: