Loading ballontranslator/ui/canvas.py +88 −19 Original line number Diff line number Diff line Loading @@ -6,10 +6,10 @@ from qtpy.QtCore import Qt, QDateTime, QRectF, QPointF, QPoint, Signal, QSizeF, from qtpy.QtGui import QPixmap, QHideEvent, QKeyEvent, QWheelEvent, QResizeEvent, QKeySequence, QPainter, QPen, QPainterPath try: from qtpy.QtWidgets import QUndoStack from qtpy.QtWidgets import QUndoStack, QUndoCommand except: from qtpy.QtGui import QUndoStack from qtpy.QtGui import QUndoStack, QUndoCommand from .misc import ndarray2pixmap from .imgtrans_proj import ProjImgTrans Loading @@ -17,7 +17,7 @@ from .textitem import TextBlkItem, TextBlock from .texteditshapecontrol import TextBlkShapeControl from .stylewidgets import FadeLabel from .image_edit import ImageEditMode, DrawingLayer, StrokeImgItem from .page_search_widget import PageSearchWidget, ReplaceOneCommand, ReplaceAllCommand from .page_search_widget import PageSearchWidget from . import constants as C CANVAS_SCALE_MAX = 3.0 Loading Loading @@ -123,8 +123,6 @@ class Canvas(QGraphicsScene): self.gv.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform) self.search_widget = PageSearchWidget(self.gv) self.search_widget.replace_one.connect(self.on_search_replace_one) self.search_widget.replace_all.connect(self.on_search_replace_all) self.ctrl_relesed = self.gv.ctrl_released self.vscroll_bar = self.gv.verticalScrollBar() Loading @@ -134,8 +132,13 @@ class Canvas(QGraphicsScene): self.rubber_band.hide() self.rubber_band_origin = None self.undoStack = QUndoStack(self) self.undoStack.indexChanged.connect(self.on_undostack_changed) self.draw_undo_stack = QUndoStack(self) self.draw_undo_stack.indexChanged.connect(self.on_drawstack_changed) self.text_undo_stack = QUndoStack(self) self.text_undo_stack.indexChanged.connect(self.on_textstack_changed) self.saved_drawundo_step = 0 self.saved_textundo_step = 0 self.scaleFactorLabel = FadeLabel(self.gv) self.scaleFactorLabel.setAlignment(Qt.AlignmentFlag.AlignCenter) self.scaleFactorLabel.setText('100%') Loading Loading @@ -174,6 +177,15 @@ class Canvas(QGraphicsScene): self.mid_btn_pressed = False self.pan_initial_pos = QPoint(0, 0) self.saved_textundo_step = 0 self.saved_drawundo_step = 0 def textEditMode(self) -> bool: return self.editor_index == 1 def drawMode(self) -> bool: return self.editor_index == 0 def scaleUp(self): self.scaleImage(1 + CANVAS_SCALE_SPEED) Loading Loading @@ -265,10 +277,10 @@ class Canvas(QGraphicsScene): if self.editing_textblkitem is not None: return super().keyPressEvent(event) if event == QKeySequence.Undo: self.undoStack.undo() self.undo() self.txtblkShapeControl.updateBoundingRect() elif event == QKeySequence.Redo: self.undoStack.redo() self.redo() self.txtblkShapeControl.updateBoundingRect() elif event.key() == Qt.Key.Key_Alt: self.alt_pressed = True Loading Loading @@ -491,10 +503,6 @@ class Canvas(QGraphicsScene): if self.stroke_img_item is not None: self.removeItem(self.stroke_img_item) def on_undostack_changed(self): if self.undoStack.count() != 0: self.setProjSaveState(True) def setProjSaveState(self, un_saved: bool): if un_saved == self.projstate_unsaved: return Loading @@ -509,10 +517,71 @@ class Canvas(QGraphicsScene): self.stroke_img_item = None self.erase_img_key = None def on_search_replace_one(self): self.undoStack.push(ReplaceOneCommand(self.search_widget)) pass def get_active_undostack(self) -> QUndoStack: if self.textEditMode(): return self.text_undo_stack elif self.drawMode(): return self.draw_undo_stack return None def push_undo_command(self, command: QUndoCommand): undo_stack = self.get_active_undostack() if undo_stack is not None: undo_stack.push(command) def on_drawstack_changed(self, index: int): if index != self.saved_drawundo_step: self.setProjSaveState(True) elif self.text_undo_stack.index() == self.saved_textundo_step: self.setProjSaveState(False) def on_textstack_changed(self, index: int): if index != self.saved_textundo_step: self.setProjSaveState(True) elif self.draw_undo_stack.index() == self.saved_drawundo_step: self.setProjSaveState(False) def redo_textedit(self): self.text_undo_stack.redo() def undo_textedit(self): self.text_undo_stack.undo() def redo(self): undo_stack = self.get_active_undostack() if undo_stack is not None: undo_stack.redo() def undo(self): undo_stack = self.get_active_undostack() if undo_stack is not None: undo_stack.undo() def clear_undostack(self, update_saved_step=False): if update_saved_step: self.saved_drawundo_step = 0 self.saved_textundo_step = 0 self.draw_undo_stack.clear() self.text_undo_stack.clear() def clear_text_stack(self): self.text_undo_stack.clear() def clear_draw_stack(self): self.draw_undo_stack.clear() def update_saved_undostep(self): self.saved_drawundo_step = self.draw_undo_stack.index() self.saved_textundo_step = self.text_undo_stack.index() def text_change_unsaved(self) -> bool: return self.saved_textundo_step == self.text_undo_stack.index() def draw_change_unsaved(self) -> bool: return self.saved_drawundo_step == self.draw_undo_stack.index() def on_search_replace_all(self): self.undoStack.push(ReplaceAllCommand(self.search_widget)) pass No newline at end of file def prepareClose(self): self.blockSignals(True) self.disconnect() self.text_undo_stack.disconnect() self.draw_undo_stack.disconnect() No newline at end of file ballontranslator/ui/drawing_commands.py 0 → 100644 +83 −0 Original line number Diff line number Diff line from qtpy.QtCore import Signal, Qt, QPointF, QSize, QLineF, QDateTime, QRectF, QPoint from qtpy.QtWidgets import QGridLayout, QPushButton, QComboBox, QSizePolicy, QBoxLayout, QCheckBox, QHBoxLayout, QGraphicsView, QStackedWidget, QVBoxLayout, QLabel, QGraphicsPixmapItem, QGraphicsEllipseItem from qtpy.QtGui import QPen, QColor, QCursor, QPainter, QPixmap, QBrush, QFontMetrics, QImage try: from qtpy.QtWidgets import QUndoCommand, QUndoStack except: from qtpy.QtGui import QUndoCommand, QUndoStack from typing import Union, Tuple, List import numpy as np import cv2 from utils.imgproc_utils import enlarge_window from utils.textblock_mask import canny_flood, connected_canny_flood from utils.logger import logger from .dl_manager import DLManager from .image_edit import ImageEditMode, PixmapItem, DrawingLayer, StrokeImgItem from .configpanel import InpaintConfigPanel from .stylewidgets import Widget, SeparatorWidget, ColorPicker, PaintQSlider from .canvas import Canvas class StrokeItemUndoCommand(QUndoCommand): def __init__(self, target_layer: DrawingLayer, rect: Tuple[int], qimg: QImage, erasing=False): super().__init__() self.qimg = qimg self.x = rect[0] self.y = rect[1] self.target_layer = target_layer self.key = str(QDateTime.currentMSecsSinceEpoch()) if erasing: self.compose_mode = QPainter.CompositionMode.CompositionMode_DestinationOut else: self.compose_mode = QPainter.CompositionMode.CompositionMode_SourceOver def undo(self): if self.qimg is not None: self.target_layer.removeQImage(self.key) self.target_layer.update() def redo(self): if self.qimg is not None: self.target_layer.addQImage(self.x, self.y, self.qimg, self.compose_mode, self.key) self.target_layer.scene().update() class InpaintUndoCommand(QUndoCommand): def __init__(self, canvas: Canvas, inpainted: np.ndarray, mask: np.ndarray, inpaint_rect: List[int]): super().__init__() self.canvas = canvas img_array = self.canvas.imgtrans_proj.inpainted_array mask_array = self.canvas.imgtrans_proj.mask_array img_view = img_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] mask_view = mask_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] self.undo_img = np.copy(img_view) self.undo_mask = np.copy(mask_view) self.redo_img = inpainted self.redo_mask = mask self.inpaint_rect = inpaint_rect def redo(self) -> None: inpaint_rect = self.inpaint_rect img_array = self.canvas.imgtrans_proj.inpainted_array mask_array = self.canvas.imgtrans_proj.mask_array img_view = img_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] mask_view = mask_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] img_view[:] = self.redo_img mask_view[:] = self.redo_mask self.canvas.setInpaintLayer() self.canvas.setMaskLayer() def undo(self) -> None: inpaint_rect = self.inpaint_rect img_array = self.canvas.imgtrans_proj.inpainted_array mask_array = self.canvas.imgtrans_proj.mask_array img_view = img_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] mask_view = mask_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] img_view[:] = self.undo_img mask_view[:] = self.undo_mask self.canvas.setInpaintLayer() self.canvas.setMaskLayer() No newline at end of file ballontranslator/ui/drawingpanel.py +12 −78 Original line number Diff line number Diff line from qtpy.QtCore import Signal, Qt, QPointF, QSize, QLineF, QDateTime, QRectF, QPoint from qtpy.QtCore import Signal, Qt, QPointF, QSize, QLineF, QRectF from qtpy.QtWidgets import QGridLayout, QPushButton, QComboBox, QSizePolicy, QBoxLayout, QCheckBox, QHBoxLayout, QGraphicsView, QStackedWidget, QVBoxLayout, QLabel, QGraphicsPixmapItem, QGraphicsEllipseItem from qtpy.QtGui import QPen, QColor, QCursor, QPainter, QPixmap, QBrush, QFontMetrics, QImage try: from qtpy.QtWidgets import QUndoCommand except: from qtpy.QtGui import QUndoCommand from qtpy.QtGui import QPen, QColor, QCursor, QPainter, QPixmap, QBrush, QFontMetrics from typing import Union, Tuple, List import numpy as np Loading @@ -16,12 +11,13 @@ from utils.textblock_mask import canny_flood, connected_canny_flood from utils.logger import logger from .dl_manager import DLManager from .image_edit import ImageEditMode, PixmapItem, DrawingLayer, StrokeImgItem from .image_edit import ImageEditMode, PixmapItem, StrokeImgItem from .configpanel import InpaintConfigPanel from .stylewidgets import Widget, SeparatorWidget, ColorPicker, PaintQSlider from .canvas import Canvas from .misc import DrawPanelConfig, ndarray2pixmap, pixmap2ndarray from .misc import DrawPanelConfig, ndarray2pixmap from .constants import CONFIG_COMBOBOX_SHORT, CONFIG_COMBOBOX_HEIGHT from .drawing_commands import InpaintUndoCommand, StrokeItemUndoCommand INPAINT_BRUSH_COLOR = QColor(127, 0, 127, 127) MAX_PEN_SIZE = 1000 Loading Loading @@ -504,7 +500,7 @@ class DrawingPanel(Widget): if self.currentTool == self.penTool: rect, _, qimg = stroke_item.clip() if rect is not None: self.canvas.undoStack.push(StrokeItemUndoCommand(self.canvas.drawingLayer, rect, qimg)) self.canvas.push_undo_command(StrokeItemUndoCommand(self.canvas.drawingLayer, rect, qimg)) self.canvas.removeItem(stroke_item) elif self.currentTool == self.inpaintTool: self.inpaint_stroke = stroke_item Loading Loading @@ -538,7 +534,7 @@ class DrawingPanel(Widget): inpaint_mask = np.zeros_like(inpainted) inpaint_mask[mask > 0] = 1 erased_img = inpaint_mask * inpainted + (1 - inpaint_mask) * origin self.canvas.undoStack.push(InpaintUndoCommand(self.canvas, erased_img, mask, inpaint_rect)) self.canvas.push_undo_command(InpaintUndoCommand(self.canvas, erased_img, mask, inpaint_rect)) self.canvas.removeItem(stroke_item) elif self.currentTool == self.penTool: Loading @@ -548,7 +544,7 @@ class DrawingPanel(Widget): self.canvas.erase_img_key = None self.canvas.stroke_img_item = None if rect is not None: self.canvas.undoStack.push(StrokeItemUndoCommand(self.canvas.drawingLayer, rect, qimg, True)) self.canvas.push_undo_command(StrokeItemUndoCommand(self.canvas.drawingLayer, rect, qimg, True)) def runInpaint(self, inpaint_dict=None): Loading Loading @@ -590,7 +586,7 @@ class DrawingPanel(Widget): inpaint_rect = inpaint_dict['inpaint_rect'] mask_array = self.canvas.imgtrans_proj.mask_array mask = cv2.bitwise_or(inpaint_dict['mask'], mask_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]]) self.canvas.undoStack.push(InpaintUndoCommand(self.canvas, inpainted, mask, inpaint_rect)) self.canvas.push_undo_command(InpaintUndoCommand(self.canvas, inpainted, mask, inpaint_rect)) self.clearInpaintItems() def on_inpaint_failed(self): Loading Loading @@ -709,7 +705,7 @@ class DrawingPanel(Widget): else: # erasing mask = np.zeros((y2 - y1, x2 - x1), dtype=np.uint8) erased = self.canvas.imgtrans_proj.img_array[y1: y2, x1: x2] self.canvas.undoStack.push(InpaintUndoCommand(self.canvas, erased, mask, [x1, y1, x2, y2])) self.canvas.push_undo_command(InpaintUndoCommand(self.canvas, erased, mask, [x1, y1, x2, y2])) self.canvas.image_edit_mode = ImageEditMode.RectTool self.setCrossCursor() Loading @@ -721,7 +717,7 @@ class DrawingPanel(Widget): ballon_mask = inpaint_dict['ballon_mask'] if not need_inpaint and self.dl_manager.dl_config.check_need_inpaint: img[np.where(ballon_mask > 0)] = bground_bgr self.canvas.undoStack.push(InpaintUndoCommand(self.canvas, img, mask, inpaint_dict['inpaint_rect'])) self.canvas.push_undo_command(InpaintUndoCommand(self.canvas, img, mask, inpaint_dict['inpaint_rect'])) self.clearInpaintItems() else: self.runInpaint(inpaint_dict=inpaint_dict) Loading Loading @@ -766,65 +762,3 @@ class DrawingPanel(Widget): self.inpaint_stroke = None if self.inpaintTool.isChecked(): self.canvas.image_edit_mode = ImageEditMode.InpaintTool No newline at end of file class StrokeItemUndoCommand(QUndoCommand): def __init__(self, target_layer: DrawingLayer, rect: Tuple[int], qimg: QImage, erasing=False): super().__init__() self.qimg = qimg self.x = rect[0] self.y = rect[1] self.target_layer = target_layer self.key = str(QDateTime.currentMSecsSinceEpoch()) if erasing: self.compose_mode = QPainter.CompositionMode.CompositionMode_DestinationOut else: self.compose_mode = QPainter.CompositionMode.CompositionMode_SourceOver def undo(self): if self.qimg is not None: self.target_layer.removeQImage(self.key) self.target_layer.update() # self.stroke_pixmap.hide() def redo(self): if self.qimg is not None: self.target_layer.addQImage(self.x, self.y, self.qimg, self.compose_mode, self.key) self.target_layer.scene().update() class InpaintUndoCommand(QUndoCommand): def __init__(self, canvas: Canvas, inpainted: np.ndarray, mask: np.ndarray, inpaint_rect: List[int]): super().__init__() self.canvas = canvas img_array = self.canvas.imgtrans_proj.inpainted_array mask_array = self.canvas.imgtrans_proj.mask_array img_view = img_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] mask_view = mask_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] self.undo_img = np.copy(img_view) self.undo_mask = np.copy(mask_view) self.redo_img = inpainted self.redo_mask = mask self.inpaint_rect = inpaint_rect def redo(self) -> None: inpaint_rect = self.inpaint_rect img_array = self.canvas.imgtrans_proj.inpainted_array mask_array = self.canvas.imgtrans_proj.mask_array img_view = img_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] mask_view = mask_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] img_view[:] = self.redo_img mask_view[:] = self.redo_mask self.canvas.setInpaintLayer() self.canvas.setMaskLayer() def undo(self) -> None: inpaint_rect = self.inpaint_rect img_array = self.canvas.imgtrans_proj.inpainted_array mask_array = self.canvas.imgtrans_proj.mask_array img_view = img_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] mask_view = mask_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] img_view[:] = self.undo_img mask_view[:] = self.undo_mask self.canvas.setInpaintLayer() self.canvas.setMaskLayer() ballontranslator/ui/global_search_widget.py +0 −4 Original line number Diff line number Diff line from qtpy.QtWidgets import QHBoxLayout, QComboBox, QSizePolicy, QLabel, QTreeView, QCheckBox, QMessageBox, QVBoxLayout, QStyle, QSlider, QStyle, QGraphicsDropShadowEffect, QWidget from qtpy.QtCore import Qt, QItemSelection, QSize, Signal, QUrl, QThread from qtpy.QtGui import QKeyEvent, QTextCursor, QStandardItemModel, QStandardItem, QFontMetrics, QColor, QShowEvent, QSyntaxHighlighter, QTextCharFormat try: from qtpy.QtWidgets import QUndoCommand except: from qtpy.QtGui import QUndoCommand from typing import List, Union, Tuple, Dict Loading ballontranslator/ui/image_edit.py +4 −1 Original line number Diff line number Diff line Loading @@ -4,6 +4,10 @@ import cv2 from qtpy.QtCore import QRectF, Qt, QPointF, QSize, QPoint, QDateTime from qtpy.QtWidgets import QStyleOptionGraphicsItem, QGraphicsPixmapItem, QWidget, QGraphicsPathItem, QGraphicsItem from qtpy.QtGui import QPen, QColor, QPainterPath, QCursor, QPainter, QPixmap, QImage, QBrush try: from qtpy.QtWidgets import QUndoCommand except: from qtpy.QtGui import QUndoCommand from .misc import DrawPanelConfig, pixmap2ndarray, ndarray2pixmap from utils.io_utils import imread, imwrite Loading Loading @@ -159,4 +163,3 @@ class DrawingLayer(QGraphicsPixmapItem): def clearAllDrawings(self): self.qimg_dict.clear() self.drawing_items_info.clear() Loading
ballontranslator/ui/canvas.py +88 −19 Original line number Diff line number Diff line Loading @@ -6,10 +6,10 @@ from qtpy.QtCore import Qt, QDateTime, QRectF, QPointF, QPoint, Signal, QSizeF, from qtpy.QtGui import QPixmap, QHideEvent, QKeyEvent, QWheelEvent, QResizeEvent, QKeySequence, QPainter, QPen, QPainterPath try: from qtpy.QtWidgets import QUndoStack from qtpy.QtWidgets import QUndoStack, QUndoCommand except: from qtpy.QtGui import QUndoStack from qtpy.QtGui import QUndoStack, QUndoCommand from .misc import ndarray2pixmap from .imgtrans_proj import ProjImgTrans Loading @@ -17,7 +17,7 @@ from .textitem import TextBlkItem, TextBlock from .texteditshapecontrol import TextBlkShapeControl from .stylewidgets import FadeLabel from .image_edit import ImageEditMode, DrawingLayer, StrokeImgItem from .page_search_widget import PageSearchWidget, ReplaceOneCommand, ReplaceAllCommand from .page_search_widget import PageSearchWidget from . import constants as C CANVAS_SCALE_MAX = 3.0 Loading Loading @@ -123,8 +123,6 @@ class Canvas(QGraphicsScene): self.gv.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform) self.search_widget = PageSearchWidget(self.gv) self.search_widget.replace_one.connect(self.on_search_replace_one) self.search_widget.replace_all.connect(self.on_search_replace_all) self.ctrl_relesed = self.gv.ctrl_released self.vscroll_bar = self.gv.verticalScrollBar() Loading @@ -134,8 +132,13 @@ class Canvas(QGraphicsScene): self.rubber_band.hide() self.rubber_band_origin = None self.undoStack = QUndoStack(self) self.undoStack.indexChanged.connect(self.on_undostack_changed) self.draw_undo_stack = QUndoStack(self) self.draw_undo_stack.indexChanged.connect(self.on_drawstack_changed) self.text_undo_stack = QUndoStack(self) self.text_undo_stack.indexChanged.connect(self.on_textstack_changed) self.saved_drawundo_step = 0 self.saved_textundo_step = 0 self.scaleFactorLabel = FadeLabel(self.gv) self.scaleFactorLabel.setAlignment(Qt.AlignmentFlag.AlignCenter) self.scaleFactorLabel.setText('100%') Loading Loading @@ -174,6 +177,15 @@ class Canvas(QGraphicsScene): self.mid_btn_pressed = False self.pan_initial_pos = QPoint(0, 0) self.saved_textundo_step = 0 self.saved_drawundo_step = 0 def textEditMode(self) -> bool: return self.editor_index == 1 def drawMode(self) -> bool: return self.editor_index == 0 def scaleUp(self): self.scaleImage(1 + CANVAS_SCALE_SPEED) Loading Loading @@ -265,10 +277,10 @@ class Canvas(QGraphicsScene): if self.editing_textblkitem is not None: return super().keyPressEvent(event) if event == QKeySequence.Undo: self.undoStack.undo() self.undo() self.txtblkShapeControl.updateBoundingRect() elif event == QKeySequence.Redo: self.undoStack.redo() self.redo() self.txtblkShapeControl.updateBoundingRect() elif event.key() == Qt.Key.Key_Alt: self.alt_pressed = True Loading Loading @@ -491,10 +503,6 @@ class Canvas(QGraphicsScene): if self.stroke_img_item is not None: self.removeItem(self.stroke_img_item) def on_undostack_changed(self): if self.undoStack.count() != 0: self.setProjSaveState(True) def setProjSaveState(self, un_saved: bool): if un_saved == self.projstate_unsaved: return Loading @@ -509,10 +517,71 @@ class Canvas(QGraphicsScene): self.stroke_img_item = None self.erase_img_key = None def on_search_replace_one(self): self.undoStack.push(ReplaceOneCommand(self.search_widget)) pass def get_active_undostack(self) -> QUndoStack: if self.textEditMode(): return self.text_undo_stack elif self.drawMode(): return self.draw_undo_stack return None def push_undo_command(self, command: QUndoCommand): undo_stack = self.get_active_undostack() if undo_stack is not None: undo_stack.push(command) def on_drawstack_changed(self, index: int): if index != self.saved_drawundo_step: self.setProjSaveState(True) elif self.text_undo_stack.index() == self.saved_textundo_step: self.setProjSaveState(False) def on_textstack_changed(self, index: int): if index != self.saved_textundo_step: self.setProjSaveState(True) elif self.draw_undo_stack.index() == self.saved_drawundo_step: self.setProjSaveState(False) def redo_textedit(self): self.text_undo_stack.redo() def undo_textedit(self): self.text_undo_stack.undo() def redo(self): undo_stack = self.get_active_undostack() if undo_stack is not None: undo_stack.redo() def undo(self): undo_stack = self.get_active_undostack() if undo_stack is not None: undo_stack.undo() def clear_undostack(self, update_saved_step=False): if update_saved_step: self.saved_drawundo_step = 0 self.saved_textundo_step = 0 self.draw_undo_stack.clear() self.text_undo_stack.clear() def clear_text_stack(self): self.text_undo_stack.clear() def clear_draw_stack(self): self.draw_undo_stack.clear() def update_saved_undostep(self): self.saved_drawundo_step = self.draw_undo_stack.index() self.saved_textundo_step = self.text_undo_stack.index() def text_change_unsaved(self) -> bool: return self.saved_textundo_step == self.text_undo_stack.index() def draw_change_unsaved(self) -> bool: return self.saved_drawundo_step == self.draw_undo_stack.index() def on_search_replace_all(self): self.undoStack.push(ReplaceAllCommand(self.search_widget)) pass No newline at end of file def prepareClose(self): self.blockSignals(True) self.disconnect() self.text_undo_stack.disconnect() self.draw_undo_stack.disconnect() No newline at end of file
ballontranslator/ui/drawing_commands.py 0 → 100644 +83 −0 Original line number Diff line number Diff line from qtpy.QtCore import Signal, Qt, QPointF, QSize, QLineF, QDateTime, QRectF, QPoint from qtpy.QtWidgets import QGridLayout, QPushButton, QComboBox, QSizePolicy, QBoxLayout, QCheckBox, QHBoxLayout, QGraphicsView, QStackedWidget, QVBoxLayout, QLabel, QGraphicsPixmapItem, QGraphicsEllipseItem from qtpy.QtGui import QPen, QColor, QCursor, QPainter, QPixmap, QBrush, QFontMetrics, QImage try: from qtpy.QtWidgets import QUndoCommand, QUndoStack except: from qtpy.QtGui import QUndoCommand, QUndoStack from typing import Union, Tuple, List import numpy as np import cv2 from utils.imgproc_utils import enlarge_window from utils.textblock_mask import canny_flood, connected_canny_flood from utils.logger import logger from .dl_manager import DLManager from .image_edit import ImageEditMode, PixmapItem, DrawingLayer, StrokeImgItem from .configpanel import InpaintConfigPanel from .stylewidgets import Widget, SeparatorWidget, ColorPicker, PaintQSlider from .canvas import Canvas class StrokeItemUndoCommand(QUndoCommand): def __init__(self, target_layer: DrawingLayer, rect: Tuple[int], qimg: QImage, erasing=False): super().__init__() self.qimg = qimg self.x = rect[0] self.y = rect[1] self.target_layer = target_layer self.key = str(QDateTime.currentMSecsSinceEpoch()) if erasing: self.compose_mode = QPainter.CompositionMode.CompositionMode_DestinationOut else: self.compose_mode = QPainter.CompositionMode.CompositionMode_SourceOver def undo(self): if self.qimg is not None: self.target_layer.removeQImage(self.key) self.target_layer.update() def redo(self): if self.qimg is not None: self.target_layer.addQImage(self.x, self.y, self.qimg, self.compose_mode, self.key) self.target_layer.scene().update() class InpaintUndoCommand(QUndoCommand): def __init__(self, canvas: Canvas, inpainted: np.ndarray, mask: np.ndarray, inpaint_rect: List[int]): super().__init__() self.canvas = canvas img_array = self.canvas.imgtrans_proj.inpainted_array mask_array = self.canvas.imgtrans_proj.mask_array img_view = img_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] mask_view = mask_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] self.undo_img = np.copy(img_view) self.undo_mask = np.copy(mask_view) self.redo_img = inpainted self.redo_mask = mask self.inpaint_rect = inpaint_rect def redo(self) -> None: inpaint_rect = self.inpaint_rect img_array = self.canvas.imgtrans_proj.inpainted_array mask_array = self.canvas.imgtrans_proj.mask_array img_view = img_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] mask_view = mask_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] img_view[:] = self.redo_img mask_view[:] = self.redo_mask self.canvas.setInpaintLayer() self.canvas.setMaskLayer() def undo(self) -> None: inpaint_rect = self.inpaint_rect img_array = self.canvas.imgtrans_proj.inpainted_array mask_array = self.canvas.imgtrans_proj.mask_array img_view = img_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] mask_view = mask_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] img_view[:] = self.undo_img mask_view[:] = self.undo_mask self.canvas.setInpaintLayer() self.canvas.setMaskLayer() No newline at end of file
ballontranslator/ui/drawingpanel.py +12 −78 Original line number Diff line number Diff line from qtpy.QtCore import Signal, Qt, QPointF, QSize, QLineF, QDateTime, QRectF, QPoint from qtpy.QtCore import Signal, Qt, QPointF, QSize, QLineF, QRectF from qtpy.QtWidgets import QGridLayout, QPushButton, QComboBox, QSizePolicy, QBoxLayout, QCheckBox, QHBoxLayout, QGraphicsView, QStackedWidget, QVBoxLayout, QLabel, QGraphicsPixmapItem, QGraphicsEllipseItem from qtpy.QtGui import QPen, QColor, QCursor, QPainter, QPixmap, QBrush, QFontMetrics, QImage try: from qtpy.QtWidgets import QUndoCommand except: from qtpy.QtGui import QUndoCommand from qtpy.QtGui import QPen, QColor, QCursor, QPainter, QPixmap, QBrush, QFontMetrics from typing import Union, Tuple, List import numpy as np Loading @@ -16,12 +11,13 @@ from utils.textblock_mask import canny_flood, connected_canny_flood from utils.logger import logger from .dl_manager import DLManager from .image_edit import ImageEditMode, PixmapItem, DrawingLayer, StrokeImgItem from .image_edit import ImageEditMode, PixmapItem, StrokeImgItem from .configpanel import InpaintConfigPanel from .stylewidgets import Widget, SeparatorWidget, ColorPicker, PaintQSlider from .canvas import Canvas from .misc import DrawPanelConfig, ndarray2pixmap, pixmap2ndarray from .misc import DrawPanelConfig, ndarray2pixmap from .constants import CONFIG_COMBOBOX_SHORT, CONFIG_COMBOBOX_HEIGHT from .drawing_commands import InpaintUndoCommand, StrokeItemUndoCommand INPAINT_BRUSH_COLOR = QColor(127, 0, 127, 127) MAX_PEN_SIZE = 1000 Loading Loading @@ -504,7 +500,7 @@ class DrawingPanel(Widget): if self.currentTool == self.penTool: rect, _, qimg = stroke_item.clip() if rect is not None: self.canvas.undoStack.push(StrokeItemUndoCommand(self.canvas.drawingLayer, rect, qimg)) self.canvas.push_undo_command(StrokeItemUndoCommand(self.canvas.drawingLayer, rect, qimg)) self.canvas.removeItem(stroke_item) elif self.currentTool == self.inpaintTool: self.inpaint_stroke = stroke_item Loading Loading @@ -538,7 +534,7 @@ class DrawingPanel(Widget): inpaint_mask = np.zeros_like(inpainted) inpaint_mask[mask > 0] = 1 erased_img = inpaint_mask * inpainted + (1 - inpaint_mask) * origin self.canvas.undoStack.push(InpaintUndoCommand(self.canvas, erased_img, mask, inpaint_rect)) self.canvas.push_undo_command(InpaintUndoCommand(self.canvas, erased_img, mask, inpaint_rect)) self.canvas.removeItem(stroke_item) elif self.currentTool == self.penTool: Loading @@ -548,7 +544,7 @@ class DrawingPanel(Widget): self.canvas.erase_img_key = None self.canvas.stroke_img_item = None if rect is not None: self.canvas.undoStack.push(StrokeItemUndoCommand(self.canvas.drawingLayer, rect, qimg, True)) self.canvas.push_undo_command(StrokeItemUndoCommand(self.canvas.drawingLayer, rect, qimg, True)) def runInpaint(self, inpaint_dict=None): Loading Loading @@ -590,7 +586,7 @@ class DrawingPanel(Widget): inpaint_rect = inpaint_dict['inpaint_rect'] mask_array = self.canvas.imgtrans_proj.mask_array mask = cv2.bitwise_or(inpaint_dict['mask'], mask_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]]) self.canvas.undoStack.push(InpaintUndoCommand(self.canvas, inpainted, mask, inpaint_rect)) self.canvas.push_undo_command(InpaintUndoCommand(self.canvas, inpainted, mask, inpaint_rect)) self.clearInpaintItems() def on_inpaint_failed(self): Loading Loading @@ -709,7 +705,7 @@ class DrawingPanel(Widget): else: # erasing mask = np.zeros((y2 - y1, x2 - x1), dtype=np.uint8) erased = self.canvas.imgtrans_proj.img_array[y1: y2, x1: x2] self.canvas.undoStack.push(InpaintUndoCommand(self.canvas, erased, mask, [x1, y1, x2, y2])) self.canvas.push_undo_command(InpaintUndoCommand(self.canvas, erased, mask, [x1, y1, x2, y2])) self.canvas.image_edit_mode = ImageEditMode.RectTool self.setCrossCursor() Loading @@ -721,7 +717,7 @@ class DrawingPanel(Widget): ballon_mask = inpaint_dict['ballon_mask'] if not need_inpaint and self.dl_manager.dl_config.check_need_inpaint: img[np.where(ballon_mask > 0)] = bground_bgr self.canvas.undoStack.push(InpaintUndoCommand(self.canvas, img, mask, inpaint_dict['inpaint_rect'])) self.canvas.push_undo_command(InpaintUndoCommand(self.canvas, img, mask, inpaint_dict['inpaint_rect'])) self.clearInpaintItems() else: self.runInpaint(inpaint_dict=inpaint_dict) Loading Loading @@ -766,65 +762,3 @@ class DrawingPanel(Widget): self.inpaint_stroke = None if self.inpaintTool.isChecked(): self.canvas.image_edit_mode = ImageEditMode.InpaintTool No newline at end of file class StrokeItemUndoCommand(QUndoCommand): def __init__(self, target_layer: DrawingLayer, rect: Tuple[int], qimg: QImage, erasing=False): super().__init__() self.qimg = qimg self.x = rect[0] self.y = rect[1] self.target_layer = target_layer self.key = str(QDateTime.currentMSecsSinceEpoch()) if erasing: self.compose_mode = QPainter.CompositionMode.CompositionMode_DestinationOut else: self.compose_mode = QPainter.CompositionMode.CompositionMode_SourceOver def undo(self): if self.qimg is not None: self.target_layer.removeQImage(self.key) self.target_layer.update() # self.stroke_pixmap.hide() def redo(self): if self.qimg is not None: self.target_layer.addQImage(self.x, self.y, self.qimg, self.compose_mode, self.key) self.target_layer.scene().update() class InpaintUndoCommand(QUndoCommand): def __init__(self, canvas: Canvas, inpainted: np.ndarray, mask: np.ndarray, inpaint_rect: List[int]): super().__init__() self.canvas = canvas img_array = self.canvas.imgtrans_proj.inpainted_array mask_array = self.canvas.imgtrans_proj.mask_array img_view = img_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] mask_view = mask_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] self.undo_img = np.copy(img_view) self.undo_mask = np.copy(mask_view) self.redo_img = inpainted self.redo_mask = mask self.inpaint_rect = inpaint_rect def redo(self) -> None: inpaint_rect = self.inpaint_rect img_array = self.canvas.imgtrans_proj.inpainted_array mask_array = self.canvas.imgtrans_proj.mask_array img_view = img_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] mask_view = mask_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] img_view[:] = self.redo_img mask_view[:] = self.redo_mask self.canvas.setInpaintLayer() self.canvas.setMaskLayer() def undo(self) -> None: inpaint_rect = self.inpaint_rect img_array = self.canvas.imgtrans_proj.inpainted_array mask_array = self.canvas.imgtrans_proj.mask_array img_view = img_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] mask_view = mask_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]] img_view[:] = self.undo_img mask_view[:] = self.undo_mask self.canvas.setInpaintLayer() self.canvas.setMaskLayer()
ballontranslator/ui/global_search_widget.py +0 −4 Original line number Diff line number Diff line from qtpy.QtWidgets import QHBoxLayout, QComboBox, QSizePolicy, QLabel, QTreeView, QCheckBox, QMessageBox, QVBoxLayout, QStyle, QSlider, QStyle, QGraphicsDropShadowEffect, QWidget from qtpy.QtCore import Qt, QItemSelection, QSize, Signal, QUrl, QThread from qtpy.QtGui import QKeyEvent, QTextCursor, QStandardItemModel, QStandardItem, QFontMetrics, QColor, QShowEvent, QSyntaxHighlighter, QTextCharFormat try: from qtpy.QtWidgets import QUndoCommand except: from qtpy.QtGui import QUndoCommand from typing import List, Union, Tuple, Dict Loading
ballontranslator/ui/image_edit.py +4 −1 Original line number Diff line number Diff line Loading @@ -4,6 +4,10 @@ import cv2 from qtpy.QtCore import QRectF, Qt, QPointF, QSize, QPoint, QDateTime from qtpy.QtWidgets import QStyleOptionGraphicsItem, QGraphicsPixmapItem, QWidget, QGraphicsPathItem, QGraphicsItem from qtpy.QtGui import QPen, QColor, QPainterPath, QCursor, QPainter, QPixmap, QImage, QBrush try: from qtpy.QtWidgets import QUndoCommand except: from qtpy.QtGui import QUndoCommand from .misc import DrawPanelConfig, pixmap2ndarray, ndarray2pixmap from utils.io_utils import imread, imwrite Loading Loading @@ -159,4 +163,3 @@ class DrawingLayer(QGraphicsPixmapItem): def clearAllDrawings(self): self.qimg_dict.clear() self.drawing_items_info.clear()