mirror of
https://github.com/rbreu/beeref.git
synced 2026-03-11 08:54:28 +00:00
Add color sampler to copy colors to clipboard in hex format
This commit is contained in:
parent
b1aec4a156
commit
7aab1ed38f
18 changed files with 577 additions and 225 deletions
|
|
@ -6,6 +6,8 @@ Added
|
|||
|
||||
* Moving the window from within BeeRef now changes to a diffent cursor from
|
||||
the default arrow cursor.
|
||||
* Added a color sampler which can copy colors from images to the
|
||||
clipboard in hex format (Images -> Sample Color)
|
||||
|
||||
|
||||
Fixed
|
||||
|
|
|
|||
|
|
@ -271,6 +271,13 @@ actions = ActionList([
|
|||
'callback': 'on_action_show_color_gamut',
|
||||
'group': 'active_when_single_image',
|
||||
}),
|
||||
Action({
|
||||
'id': 'sample_color',
|
||||
'text': 'Sample Color',
|
||||
'shortcuts': ['S'],
|
||||
'callback': 'on_action_sample_color',
|
||||
'group': 'active_when_items_in_scene',
|
||||
}),
|
||||
Action({
|
||||
'id': 'crop',
|
||||
'text': '&Crop',
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ menu_structure = [
|
|||
'grayscale',
|
||||
MENU_SEPARATOR,
|
||||
'show_color_gamut',
|
||||
'sample_color',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class ExporterBase:
|
|||
|
||||
def __init__(self, scene):
|
||||
self.scene = scene
|
||||
self.scene.cancel_crop_mode()
|
||||
self.scene.cancel_active_modes()
|
||||
self.scene.deselect_all_items()
|
||||
# Selection outlines/handles will be rendered to the exported
|
||||
# image, so deselect first. (Alternatively, pass an attribute
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@ def register_item(cls):
|
|||
class BeeItemMixin(SelectableMixin):
|
||||
"""Base for all items added by the user."""
|
||||
|
||||
def sample_color_at(self, pos):
|
||||
return None
|
||||
|
||||
def set_pos_center(self, pos):
|
||||
"""Sets the position using the item's center as the origin point."""
|
||||
|
||||
|
|
@ -64,7 +67,7 @@ class BeeItemMixin(SelectableMixin):
|
|||
def on_selected_change(self, value):
|
||||
if (value and self.scene()
|
||||
and not self.scene().has_selection()
|
||||
and not self.scene().rubberband_active):
|
||||
and not self.scene().active_mode is None):
|
||||
self.bring_to_front()
|
||||
|
||||
def update_from_data(self, **kwargs):
|
||||
|
|
@ -170,6 +173,18 @@ class BeePixmapItem(BeeItemMixin, QtWidgets.QGraphicsPixmapItem):
|
|||
|
||||
self.update()
|
||||
|
||||
def sample_color_at(self, pos):
|
||||
ipos = self.mapFromScene(pos)
|
||||
if self.grayscale:
|
||||
pm = self._grayscale_pixmap
|
||||
else:
|
||||
pm = self.pixmap()
|
||||
img = pm.toImage()
|
||||
|
||||
color = img.pixelColor(int(ipos.x()), int(ipos.y()))
|
||||
if color.alpha():
|
||||
return color
|
||||
|
||||
def bounding_rect_unselected(self):
|
||||
if self.crop_mode:
|
||||
return QtWidgets.QGraphicsPixmapItem.boundingRect(self)
|
||||
|
|
@ -414,9 +429,7 @@ class BeePixmapItem(BeeItemMixin, QtWidgets.QGraphicsPixmapItem):
|
|||
color = QtGui.QColor(0, 0, 0)
|
||||
color.setAlpha(100)
|
||||
painter.setBrush(QtGui.QBrush(color))
|
||||
pen = QtGui.QPen()
|
||||
pen.setWidth(0)
|
||||
painter.setPen(pen)
|
||||
painter.setPen(Qt.PenStyle.NoPen)
|
||||
painter.drawPath(path)
|
||||
painter.setBrush(QtGui.QBrush())
|
||||
|
||||
|
|
|
|||
|
|
@ -35,10 +35,12 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
|
|||
cursor_changed = QtCore.pyqtSignal(QtGui.QCursor)
|
||||
cursor_cleared = QtCore.pyqtSignal()
|
||||
|
||||
MOVE_MODE = 1
|
||||
RUBBERBAND_MODE = 2
|
||||
|
||||
def __init__(self, undo_stack):
|
||||
super().__init__()
|
||||
self.move_active = False
|
||||
self.rubberband_active = False
|
||||
self.active_mode = None
|
||||
self.undo_stack = undo_stack
|
||||
self.max_z = 0
|
||||
self.min_z = 0
|
||||
|
|
@ -66,6 +68,19 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
|
|||
logger.debug(f'Removing item {item}')
|
||||
super().removeItem(item)
|
||||
|
||||
def cancel_active_modes(self):
|
||||
"""Cancels ongoing crop modes, rubberband modes etc, if there are
|
||||
any.
|
||||
"""
|
||||
self.cancel_crop_mode()
|
||||
self.end_rubberband_mode()
|
||||
|
||||
def end_rubberband_mode(self):
|
||||
if self.rubberband_item.scene():
|
||||
logger.debug('Ending rubberband selection')
|
||||
self.removeItem(self.rubberband_item)
|
||||
self.active_mode = None
|
||||
|
||||
def cancel_crop_mode(self):
|
||||
"""Cancels an ongoing crop mode, if there is any."""
|
||||
if self.crop_item:
|
||||
|
|
@ -84,7 +99,7 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
|
|||
self.undo_stack.push(commands.InsertItems(self, copies, position))
|
||||
|
||||
def raise_to_top(self):
|
||||
self.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
items = self.selectedItems(user_only=True)
|
||||
z_values = map(lambda i: i.zValue(), items)
|
||||
delta = self.max_z + self.Z_STEP - min(z_values)
|
||||
|
|
@ -93,7 +108,7 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
|
|||
item.setZValue(item.zValue() + delta)
|
||||
|
||||
def lower_to_bottom(self):
|
||||
self.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
items = self.selectedItems(user_only=True)
|
||||
z_values = map(lambda i: i.zValue(), items)
|
||||
delta = self.min_z - self.Z_STEP - max(z_values)
|
||||
|
|
@ -109,7 +124,7 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
|
|||
:param mode: "width" or "height".
|
||||
"""
|
||||
|
||||
self.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
values = []
|
||||
items = self.selectedItems(user_only=True)
|
||||
for item in items:
|
||||
|
|
@ -141,7 +156,7 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
|
|||
Size meaning the area = widh * height.
|
||||
"""
|
||||
|
||||
self.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
sizes = []
|
||||
items = self.selectedItems(user_only=True)
|
||||
for item in items:
|
||||
|
|
@ -164,7 +179,7 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
|
|||
def arrange(self, vertical=False):
|
||||
"""Arrange items in a line (horizontally or vertically)."""
|
||||
|
||||
self.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
|
||||
items = self.selectedItems(user_only=True)
|
||||
if len(items) < 2:
|
||||
|
|
@ -205,7 +220,7 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
|
|||
positions))
|
||||
|
||||
def arrange_optimal(self):
|
||||
self.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
|
||||
items = self.selectedItems(user_only=True)
|
||||
if len(items) < 2:
|
||||
|
|
@ -243,7 +258,7 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
|
|||
|
||||
def flip_items(self, vertical=False):
|
||||
"""Flip selected items."""
|
||||
self.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
self.undo_stack.push(
|
||||
commands.FlipItems(self.selectedItems(user_only=True),
|
||||
self.get_selection_center(),
|
||||
|
|
@ -259,15 +274,20 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
|
|||
if item.is_image:
|
||||
item.enter_crop_mode()
|
||||
|
||||
def sample_color_at(self, position):
|
||||
item_at_pos = self.itemAt(position, self.views()[0].transform())
|
||||
if item_at_pos:
|
||||
return item_at_pos.sample_color_at(position)
|
||||
|
||||
def select_all_items(self):
|
||||
self.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
path = QtGui.QPainterPath()
|
||||
path.addRect(self.itemsBoundingRect())
|
||||
# This is faster than looping through all items and calling setSelected
|
||||
self.setSelectionArea(path)
|
||||
|
||||
def deselect_all_items(self):
|
||||
self.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
self.clearSelection()
|
||||
|
||||
def has_selection(self):
|
||||
|
|
@ -316,16 +336,16 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
|
|||
super().mousePressEvent(event)
|
||||
return
|
||||
if item_at_pos:
|
||||
self.move_active = True
|
||||
self.active_mode = self.MOVE_MODE
|
||||
elif self.items():
|
||||
self.rubberband_active = True
|
||||
self.active_mode = self.RUBBERBAND_MODE
|
||||
|
||||
super().mousePressEvent(event)
|
||||
|
||||
def mouseDoubleClickEvent(self, event):
|
||||
self.cancel_active_modes()
|
||||
item = self.itemAt(event.scenePos(), self.views()[0].transform())
|
||||
if item:
|
||||
self.move_active = False
|
||||
if not item.isSelected():
|
||||
item.setSelected(True)
|
||||
if item.is_editable:
|
||||
|
|
@ -339,7 +359,7 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
|
|||
super().mouseDoubleClickEvent(event)
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
if self.rubberband_active:
|
||||
if self.active_mode == self.RUBBERBAND_MODE:
|
||||
if not self.rubberband_item.scene():
|
||||
logger.debug('Activating rubberband selection')
|
||||
self.addItem(self.rubberband_item)
|
||||
|
|
@ -350,22 +370,19 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
|
|||
super().mouseMoveEvent(event)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
if self.rubberband_active:
|
||||
if self.rubberband_item.scene():
|
||||
logger.debug('Ending rubberband selection')
|
||||
self.removeItem(self.rubberband_item)
|
||||
self.rubberband_active = False
|
||||
if (self.move_active
|
||||
if self.active_mode == self.RUBBERBAND_MODE:
|
||||
self.end_rubberband_mode()
|
||||
if (self.active_mode == self.MOVE_MODE
|
||||
and self.has_selection()
|
||||
and not self.multi_select_item.is_action_active()
|
||||
and not self.selectedItems()[0].is_action_active()):
|
||||
and self.multi_select_item.active_mode is None
|
||||
and self.selectedItems()[0].active_mode is None):
|
||||
delta = event.scenePos() - self.event_start
|
||||
if not delta.isNull():
|
||||
self.undo_stack.push(
|
||||
commands.MoveItemsBy(self.selectedItems(),
|
||||
delta,
|
||||
ignore_first_redo=True))
|
||||
self.move_active = False
|
||||
self.active_mode = None
|
||||
super().mouseReleaseEvent(event)
|
||||
|
||||
def selectedItems(self, user_only=False):
|
||||
|
|
@ -446,8 +463,7 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
|
|||
|
||||
def on_change(self, region):
|
||||
if (self.multi_select_item.scene()
|
||||
and not self.multi_select_item.scale_active
|
||||
and not self.multi_select_item.rotate_active):
|
||||
and self.multi_select_item.active_mode is None):
|
||||
self.multi_select_item.fit_selection_area(
|
||||
self.itemsBoundingRect(selection_only=True))
|
||||
|
||||
|
|
|
|||
|
|
@ -140,6 +140,10 @@ class SelectableMixin(BaseItemMixin):
|
|||
SELECT_ROTATE_SIZE = 10 # size of hover area for rotating
|
||||
SELECT_FREE_CENTER = 20 # size of handle-free area in the center
|
||||
|
||||
SCALE_MODE = 1
|
||||
ROTATE_MODE = 2
|
||||
FLIP_MODE = 3
|
||||
|
||||
def init_selectable(self):
|
||||
self.setAcceptHoverEvents(True)
|
||||
self.setFlags(
|
||||
|
|
@ -147,19 +151,9 @@ class SelectableMixin(BaseItemMixin):
|
|||
| QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemIsSelectable)
|
||||
|
||||
self.viewport_scale = 1
|
||||
self.reset_actions()
|
||||
self.active_mode = None
|
||||
self.is_editable = False
|
||||
|
||||
def reset_actions(self):
|
||||
self.scale_active = False
|
||||
self.rotate_active = False
|
||||
self.flip_active = False
|
||||
|
||||
def is_action_active(self):
|
||||
return any((self.scale_active,
|
||||
self.rotate_active,
|
||||
self.flip_active))
|
||||
|
||||
def fixed_length_for_viewport(self, value):
|
||||
"""The interactable areas need to stay the same size on the
|
||||
screen so we need to adjust the values according to the scale
|
||||
|
|
@ -423,7 +417,7 @@ class SelectableMixin(BaseItemMixin):
|
|||
# Check if we are in one of the corner's scale areas
|
||||
if self.get_scale_bounds(corner).contains(event.pos()):
|
||||
# Start scale action for this corner
|
||||
self.scale_active = True
|
||||
self.active_mode = self.SCALE_MODE
|
||||
self.event_direction = self.get_direction_from_center(
|
||||
event.scenePos())
|
||||
self.event_anchor = self.mapToScene(
|
||||
|
|
@ -435,7 +429,7 @@ class SelectableMixin(BaseItemMixin):
|
|||
# Check if we are in one of the corner's rotate areas
|
||||
if self.get_rotate_bounds(corner).contains(event.pos()):
|
||||
# Start rotate action
|
||||
self.rotate_active = True
|
||||
self.active_mode = self.ROTATE_MODE
|
||||
self.event_anchor = self.center_scene_coords
|
||||
self.rotate_start_angle = self.get_rotate_angle(
|
||||
event.scenePos())
|
||||
|
|
@ -446,7 +440,7 @@ class SelectableMixin(BaseItemMixin):
|
|||
# Check if we are in one of the flip edges:
|
||||
for edge in self.get_flip_bounds():
|
||||
if edge['rect'].contains(event.pos()):
|
||||
self.flip_active = True
|
||||
self.active_mode = self.FLIP_MODE
|
||||
event.accept()
|
||||
self.scene().undo_stack.push(
|
||||
commands.FlipItems(
|
||||
|
|
@ -549,14 +543,14 @@ class SelectableMixin(BaseItemMixin):
|
|||
if (event.scenePos() - self.event_start).manhattanLength() > 5:
|
||||
self.scene().views()[0].reset_previous_transform()
|
||||
|
||||
if self.scale_active:
|
||||
if self.active_mode == self.SCALE_MODE:
|
||||
factor = self.get_scale_factor(event)
|
||||
for item in self.selection_action_items():
|
||||
item.setScale(item.scale_orig_factor * factor,
|
||||
item.mapFromScene(self.event_anchor))
|
||||
event.accept()
|
||||
return
|
||||
if self.rotate_active:
|
||||
if self.active_mode == self.ROTATE_MODE:
|
||||
snap = (event.modifiers() == Qt.KeyboardModifier.ControlModifier
|
||||
or event.modifiers() == Qt.KeyboardModifier.ShiftModifier)
|
||||
delta = self.get_rotate_delta(event.scenePos(), snap)
|
||||
|
|
@ -566,7 +560,7 @@ class SelectableMixin(BaseItemMixin):
|
|||
item.mapFromScene(self.event_anchor))
|
||||
event.accept()
|
||||
return
|
||||
if self.flip_active:
|
||||
if self.active_mode == self.FLIP_MODE:
|
||||
# We have already flipped on MousePress, but we
|
||||
# still need to accept the event here as to not
|
||||
# initiate an item move
|
||||
|
|
@ -576,7 +570,7 @@ class SelectableMixin(BaseItemMixin):
|
|||
super().mouseMoveEvent(event)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
if self.scale_active:
|
||||
if self.active_mode == self.SCALE_MODE:
|
||||
if self.get_scale_factor(event) != 1:
|
||||
self.scene().undo_stack.push(
|
||||
commands.ScaleItemsBy(
|
||||
|
|
@ -585,9 +579,9 @@ class SelectableMixin(BaseItemMixin):
|
|||
self.event_anchor,
|
||||
ignore_first_redo=True))
|
||||
event.accept()
|
||||
self.reset_actions()
|
||||
self.active_mode = None
|
||||
return
|
||||
elif self.rotate_active:
|
||||
elif self.active_mode == self.ROTATE_MODE:
|
||||
self.scene().on_selection_change()
|
||||
if self.get_rotate_delta(event.scenePos()) != 0:
|
||||
self.scene().undo_stack.push(
|
||||
|
|
@ -597,18 +591,18 @@ class SelectableMixin(BaseItemMixin):
|
|||
self.event_anchor,
|
||||
ignore_first_redo=True))
|
||||
event.accept()
|
||||
self.reset_actions()
|
||||
self.active_mode = None
|
||||
return
|
||||
elif self.flip_active:
|
||||
elif self.active_mode == self.FLIP_MODE:
|
||||
for edge in self.get_flip_bounds():
|
||||
if edge['rect'].contains(event.pos()):
|
||||
# We have already flipped on MousePress, but we
|
||||
# still need to accept the event here as to not
|
||||
# initiate an item move
|
||||
event.accept()
|
||||
self.reset_actions()
|
||||
self.active_mode = None
|
||||
return
|
||||
self.reset_actions()
|
||||
self.active_mode = None
|
||||
super().mouseReleaseEvent(event)
|
||||
|
||||
def on_view_scale_change(self):
|
||||
|
|
|
|||
|
|
@ -78,3 +78,18 @@ def get_file_extension_from_format(formatstr):
|
|||
extensions = re.match(r'.* \((.*)\)', formatstr).groups()[0]
|
||||
ext = extensions.split()[0]
|
||||
return ext.removeprefix('*.')
|
||||
|
||||
|
||||
def qcolor_to_hex(color):
|
||||
"""Returns the QColor as a hex represenation string:
|
||||
#RRGGBBAA if the color has transparencey, otherwise #RRGGBB.
|
||||
"""
|
||||
|
||||
if color.alpha() == 255:
|
||||
return color.name()
|
||||
|
||||
# The name method can only do HexRgb and HexArgb, not HexRgba, so
|
||||
# we have to do this ourselves:
|
||||
rgb = color.name()
|
||||
alpha = hex(color.alpha()).removeprefix('0x')
|
||||
return f'{rgb}{alpha}'
|
||||
|
|
|
|||
111
beeref/view.py
111
beeref/view.py
|
|
@ -31,7 +31,7 @@ from beeref import widgets
|
|||
from beeref.items import BeePixmapItem, BeeTextItem
|
||||
from beeref.main_controls import MainControlsMixin
|
||||
from beeref.scene import BeeGraphicsScene
|
||||
from beeref.utils import get_file_extension_from_format
|
||||
from beeref.utils import get_file_extension_from_format, qcolor_to_hex
|
||||
|
||||
|
||||
commandline_args = CommandlineArgs()
|
||||
|
|
@ -42,6 +42,10 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
QtWidgets.QGraphicsView,
|
||||
ActionsMixin):
|
||||
|
||||
PAN_MODE = 1
|
||||
ZOOM_MODE = 2
|
||||
SAMPLE_COLOR_MODE = 3
|
||||
|
||||
def __init__(self, app, parent=None):
|
||||
super().__init__(parent)
|
||||
self.app = app
|
||||
|
|
@ -62,8 +66,7 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
|
||||
self.filename = None
|
||||
self.previous_transform = None
|
||||
self.pan_active = False
|
||||
self.zoom_active = False
|
||||
self.active_mode = None
|
||||
|
||||
self.scene = BeeGraphicsScene(self.undo_stack)
|
||||
self.scene.changed.connect(self.on_scene_changed)
|
||||
|
|
@ -94,6 +97,19 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
self.settings.update_recent_files(value)
|
||||
self.update_menu_and_actions()
|
||||
|
||||
def cancel_active_modes(self):
|
||||
self.scene.cancel_active_modes()
|
||||
self.cancel_sample_color_mode()
|
||||
self.active_mode = None
|
||||
|
||||
def cancel_sample_color_mode(self):
|
||||
logger.debug('Cancel sample color mode')
|
||||
self.active_mode = None
|
||||
self.viewport().unsetCursor()
|
||||
if hasattr(self, 'sample_color_widget'):
|
||||
self.sample_color_widget.hide()
|
||||
del self.sample_color_widget
|
||||
|
||||
def update_window_title(self):
|
||||
clean = self.undo_stack.isClean()
|
||||
if clean and not self.filename:
|
||||
|
|
@ -145,6 +161,7 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
|
||||
def clear_scene(self):
|
||||
logging.debug('Clearing scene...')
|
||||
self.cancel_active_modes()
|
||||
self.scene.clear()
|
||||
self.undo_stack.clear()
|
||||
self.filename = None
|
||||
|
|
@ -231,12 +248,12 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
|
||||
def on_action_undo(self):
|
||||
logger.debug('Undo: %s' % self.undo_stack.undoText())
|
||||
self.scene.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
self.undo_stack.undo()
|
||||
|
||||
def on_action_redo(self):
|
||||
logger.debug('Redo: %s' % self.undo_stack.redoText())
|
||||
self.scene.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
self.undo_stack.redo()
|
||||
|
||||
def on_action_select_all(self):
|
||||
|
|
@ -247,7 +264,7 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
|
||||
def on_action_delete_items(self):
|
||||
logger.debug('Deleting items...')
|
||||
self.scene.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
self.undo_stack.push(
|
||||
commands.DeleteItems(
|
||||
self.scene, self.scene.selectedItems(user_only=True)))
|
||||
|
|
@ -307,33 +324,45 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
self.scene.flip_items(vertical=True)
|
||||
|
||||
def on_action_reset_scale(self):
|
||||
self.scene.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
self.undo_stack.push(commands.ResetScale(
|
||||
self.scene.selectedItems(user_only=True)))
|
||||
|
||||
def on_action_reset_rotation(self):
|
||||
self.scene.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
self.undo_stack.push(commands.ResetRotation(
|
||||
self.scene.selectedItems(user_only=True)))
|
||||
|
||||
def on_action_reset_flip(self):
|
||||
self.scene.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
self.undo_stack.push(commands.ResetFlip(
|
||||
self.scene.selectedItems(user_only=True)))
|
||||
|
||||
def on_action_reset_crop(self):
|
||||
self.scene.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
self.undo_stack.push(commands.ResetCrop(
|
||||
self.scene.selectedItems(user_only=True)))
|
||||
|
||||
def on_action_reset_transforms(self):
|
||||
self.scene.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
self.undo_stack.push(commands.ResetTransforms(
|
||||
self.scene.selectedItems(user_only=True)))
|
||||
|
||||
def on_action_show_color_gamut(self):
|
||||
widgets.color_gamut.GamutDialog(self, self.scene.selectedItems()[0])
|
||||
|
||||
def on_action_sample_color(self):
|
||||
self.cancel_active_modes()
|
||||
logger.debug('Entering sample color mode')
|
||||
self.viewport().setCursor(Qt.CursorShape.CrossCursor)
|
||||
self.active_mode = self.SAMPLE_COLOR_MODE
|
||||
|
||||
pos = self.mapFromGlobal(self.cursor().pos())
|
||||
self.sample_color_widget = widgets.SampleColorWidget(
|
||||
self,
|
||||
pos,
|
||||
self.scene.sample_color_at(self.mapToScene(pos)))
|
||||
|
||||
def on_items_loaded(self, value):
|
||||
logger.debug('On items loaded: add queued items')
|
||||
self.scene.add_queued_items()
|
||||
|
|
@ -364,7 +393,7 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
self.worker.start()
|
||||
|
||||
def on_action_open(self):
|
||||
self.scene.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
filename, f = QtWidgets.QFileDialog.getOpenFileName(
|
||||
parent=self,
|
||||
caption='Open file',
|
||||
|
|
@ -398,7 +427,7 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
self.worker.start()
|
||||
|
||||
def on_action_save_as(self):
|
||||
self.scene.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
directory = os.path.dirname(self.filename) if self.filename else None
|
||||
filename, f = QtWidgets.QFileDialog.getSaveFileName(
|
||||
parent=self,
|
||||
|
|
@ -409,7 +438,7 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
self.do_save(filename, create_new=True)
|
||||
|
||||
def on_action_save(self):
|
||||
self.scene.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
if not self.filename:
|
||||
self.on_action_save_as()
|
||||
else:
|
||||
|
|
@ -528,6 +557,7 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
self.worker.start()
|
||||
|
||||
def on_action_insert_images(self):
|
||||
self.cancel_active_modes()
|
||||
formats = self.get_supported_image_formats(QtGui.QImageReader)
|
||||
logger.debug(f'Supported image types for reading: {formats}')
|
||||
filenames, f = QtWidgets.QFileDialog.getOpenFileNames(
|
||||
|
|
@ -537,6 +567,7 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
self.do_insert_images(filenames)
|
||||
|
||||
def on_action_insert_text(self):
|
||||
self.cancel_active_modes()
|
||||
item = BeeTextItem()
|
||||
pos = self.mapToScene(self.mapFromGlobal(self.cursor().pos()))
|
||||
item.setScale(1 / self.get_scale())
|
||||
|
|
@ -544,7 +575,7 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
|
||||
def on_action_copy(self):
|
||||
logger.debug('Copying to clipboard...')
|
||||
self.scene.cancel_crop_mode()
|
||||
self.cancel_active_modes()
|
||||
clipboard = QtWidgets.QApplication.clipboard()
|
||||
items = self.scene.selectedItems(user_only=True)
|
||||
|
||||
|
|
@ -562,6 +593,7 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
'beeref/items', QtCore.QByteArray.number(len(items)))
|
||||
|
||||
def on_action_paste(self):
|
||||
self.cancel_active_modes()
|
||||
logger.debug('Pasting from clipboard...')
|
||||
clipboard = QtWidgets.QApplication.clipboard()
|
||||
pos = self.mapToScene(self.mapFromGlobal(self.cursor().pos()))
|
||||
|
|
@ -611,11 +643,11 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
self.viewport().repaint()
|
||||
|
||||
def on_cursor_changed(self, cursor):
|
||||
if not self.pan_active:
|
||||
if self.active_mode is None:
|
||||
self.viewport().setCursor(cursor)
|
||||
|
||||
def on_cursor_cleared(self):
|
||||
if not self.pan_active:
|
||||
if self.active_mode is None:
|
||||
self.viewport().unsetCursor()
|
||||
|
||||
def recalc_scene_rect(self):
|
||||
|
|
@ -720,9 +752,27 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
if self.mousePressEventMainControls(event):
|
||||
return
|
||||
|
||||
if self.active_mode == self.SAMPLE_COLOR_MODE:
|
||||
if (event.button() == Qt.MouseButton.LeftButton):
|
||||
color = self.scene.sample_color_at(
|
||||
self.mapToScene(event.pos()))
|
||||
if color:
|
||||
name = qcolor_to_hex(color)
|
||||
clipboard = QtWidgets.QApplication.clipboard()
|
||||
clipboard.setText(name)
|
||||
self.scene.internal_clipboard = []
|
||||
msg = f'Copied color to clipboard: {name}'
|
||||
logger.debug(msg)
|
||||
widgets.BeeNotification(self, msg)
|
||||
else:
|
||||
logger.debug('No color found')
|
||||
self.cancel_sample_color_mode()
|
||||
event.accept()
|
||||
return
|
||||
|
||||
if (event.button() == Qt.MouseButton.MiddleButton
|
||||
and event.modifiers() == Qt.KeyboardModifier.ControlModifier):
|
||||
self.zoom_active = True
|
||||
self.active_mode = self.ZOOM_MODE
|
||||
self.event_start = event.position()
|
||||
self.event_anchor = event.position()
|
||||
event.accept()
|
||||
|
|
@ -732,7 +782,7 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
or (event.button() == Qt.MouseButton.LeftButton
|
||||
and event.modifiers() == Qt.KeyboardModifier.AltModifier)):
|
||||
logger.trace('Begin pan')
|
||||
self.pan_active = True
|
||||
self.active_mode = self.PAN_MODE
|
||||
self.event_start = event.position()
|
||||
self.viewport().setCursor(Qt.CursorShape.ClosedHandCursor)
|
||||
# ClosedHandCursor and OpenHandCursor don't work, but I
|
||||
|
|
@ -744,7 +794,7 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
super().mousePressEvent(event)
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
if self.pan_active:
|
||||
if self.active_mode == self.PAN_MODE:
|
||||
self.reset_previous_transform()
|
||||
pos = event.position()
|
||||
self.pan(self.event_start - pos)
|
||||
|
|
@ -752,7 +802,7 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
event.accept()
|
||||
return
|
||||
|
||||
if self.zoom_active:
|
||||
if self.active_mode == self.ZOOM_MODE:
|
||||
self.reset_previous_transform()
|
||||
pos = event.position()
|
||||
delta = (self.event_start - pos).y()
|
||||
|
|
@ -761,19 +811,26 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
event.accept()
|
||||
return
|
||||
|
||||
if self.active_mode == self.SAMPLE_COLOR_MODE:
|
||||
self.sample_color_widget.update(
|
||||
event.position(),
|
||||
self.scene.sample_color_at(self.mapToScene(event.pos())))
|
||||
event.accept()
|
||||
return
|
||||
|
||||
if self.mouseMoveEventMainControls(event):
|
||||
return
|
||||
super().mouseMoveEvent(event)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
if self.pan_active:
|
||||
if self.active_mode == self.PAN_MODE:
|
||||
logger.trace('End pan')
|
||||
self.viewport().unsetCursor()
|
||||
self.pan_active = False
|
||||
self.active_mode = None
|
||||
event.accept()
|
||||
return
|
||||
if self.zoom_active:
|
||||
self.zoom_active = False
|
||||
if self.active_mode == self.ZOOM_MODE:
|
||||
self.active_mode = None
|
||||
event.accept()
|
||||
return
|
||||
if self.mouseReleaseEventMainControls(event):
|
||||
|
|
@ -788,4 +845,8 @@ class BeeGraphicsView(MainControlsMixin,
|
|||
def keyPressEvent(self, event):
|
||||
if self.keyPressEventMainControls(event):
|
||||
return
|
||||
if self.active_mode == self.SAMPLE_COLOR_MODE:
|
||||
self.cancel_sample_color_mode()
|
||||
event.accept()
|
||||
return
|
||||
super().keyPressEvent(event)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
import logging
|
||||
import os.path
|
||||
|
||||
from PyQt6 import QtCore, QtWidgets
|
||||
from PyQt6 import QtCore, QtWidgets, QtGui
|
||||
from PyQt6.QtCore import Qt
|
||||
|
||||
from beeref import constants, commands
|
||||
|
|
@ -239,3 +239,57 @@ class ChangeOpacityDialog(QtWidgets.QDialog):
|
|||
def reject(self):
|
||||
self.command.undo()
|
||||
return super().reject()
|
||||
|
||||
|
||||
class BeeNotification(QtWidgets.QWidget):
|
||||
def __init__(self, parent, text):
|
||||
super().__init__(parent)
|
||||
self.label = QtWidgets.QLabel(text)
|
||||
self.setObjectName('BeeNotification')
|
||||
self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
|
||||
self.setAutoFillBackground(True)
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
layout.addWidget(self.label)
|
||||
self.setLayout(layout)
|
||||
color = constants.COLORS['Active:Window']
|
||||
self.setStyleSheet(
|
||||
f'background-color: rgba({color[0]}, {color[1]}, {color[2]}, 0.9);'
|
||||
'padding: 0.7em;'
|
||||
'border-radius: 5px;')
|
||||
self.show()
|
||||
# We only get own width after showing it;
|
||||
# updateGeometry doesn't work on hidden widgets
|
||||
x = (parent.width() - self.width()) / 2
|
||||
self.move(int(x), 10)
|
||||
|
||||
QtCore.QTimer.singleShot(1000 * 3, self.deleteLater)
|
||||
|
||||
|
||||
class SampleColorWidget(QtWidgets.QWidget):
|
||||
|
||||
OFFSET = 10 # Offset from mouse pointer
|
||||
SIZE = 50
|
||||
NONE_COLOR = QtGui.QColor(0, 0, 0, 0)
|
||||
|
||||
def __init__(self, parent, pos, color):
|
||||
super().__init__(parent)
|
||||
self.color = color
|
||||
self.set_pos(pos)
|
||||
self.show()
|
||||
|
||||
def set_pos(self, pos):
|
||||
self.setGeometry(int(pos.x() + self.OFFSET),
|
||||
int(pos.y() + self.OFFSET),
|
||||
self.SIZE, self.SIZE)
|
||||
|
||||
def paintEvent(self, event):
|
||||
color = self.color if self.color else self.NONE_COLOR
|
||||
painter = QtGui.QPainter(self)
|
||||
painter.setBrush(QtGui.QBrush(color))
|
||||
painter.setPen(Qt.PenStyle.NoPen)
|
||||
painter.drawRect(0, 0, self.SIZE, self.SIZE)
|
||||
|
||||
def update(self, pos, color):
|
||||
self.set_pos(pos)
|
||||
self.color = color
|
||||
self.repaint()
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ class WelcomeOverlay(MainControlsMixin, QtWidgets.QWidget):
|
|||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
self.control_target = parent
|
||||
self.setAttribute(Qt.WidgetAttribute.WA_NoSystemBackground)
|
||||
self.setAutoFillBackground(True)
|
||||
self.init_main_controls(main_window=parent.parent)
|
||||
|
||||
# Recent files
|
||||
|
|
|
|||
|
|
@ -846,3 +846,42 @@ def test_mouse_release_event_when_not_crop_mode(mouse_mock, qapp, item):
|
|||
assert item.crop_mode is False
|
||||
event.accept.assert_not_called()
|
||||
mouse_mock.assert_called_once_with(event)
|
||||
|
||||
|
||||
def test_sample_color_at_returns_color(qapp, view):
|
||||
color = QtGui.QColor(255, 0, 0, 3)
|
||||
img = QtGui.QImage(10, 10, QtGui.QImage.Format.Format_ARGB32)
|
||||
img.fill(color)
|
||||
item = BeePixmapItem(img, 'foo.png')
|
||||
view.scene.addItem(item)
|
||||
assert item.sample_color_at(QtCore.QPointF(2, 2)) == color
|
||||
|
||||
|
||||
def test_sample_color_at_returns_none_when_fully_transparent(qapp, view):
|
||||
color = QtGui.QColor(255, 0, 0, 0)
|
||||
img = QtGui.QImage(10, 10, QtGui.QImage.Format.Format_ARGB32)
|
||||
img.fill(color)
|
||||
item = BeePixmapItem(img, 'foo.png')
|
||||
view.scene.addItem(item)
|
||||
assert item.sample_color_at(QtCore.QPointF(2, 2)) is None
|
||||
|
||||
|
||||
def test_sample_color_in_greyscale_mode(qapp, view):
|
||||
color = QtGui.QColor(255, 0, 0)
|
||||
img = QtGui.QImage(10, 10, QtGui.QImage.Format.Format_ARGB32)
|
||||
img.fill(color)
|
||||
item = BeePixmapItem(img, 'foo.png')
|
||||
item.grayscale = True
|
||||
view.scene.addItem(item)
|
||||
gray = item.sample_color_at(QtCore.QPointF(2, 2))
|
||||
print(gray.red(), gray.green(), gray.blue(), gray.alpha())
|
||||
assert gray == QtGui.QColor(130, 130, 130)
|
||||
|
||||
|
||||
def test_sample_color_at_returns_none_when_transparent(qapp, view):
|
||||
color = QtGui.QColor(255, 0, 0, 0)
|
||||
img = QtGui.QImage(10, 10, QtGui.QImage.Format.Format_ARGB32)
|
||||
img.fill(color)
|
||||
item = BeePixmapItem(img, 'foo.png')
|
||||
view.scene.addItem(item)
|
||||
assert item.sample_color_at(QtCore.QPointF(2, 2)) is None
|
||||
|
|
|
|||
|
|
@ -24,6 +24,12 @@ def test_init(selectable_mock, qapp):
|
|||
selectable_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_sample_color_at(qapp, view):
|
||||
item = BeeTextItem('foo bar')
|
||||
view.scene.addItem(item)
|
||||
assert item.sample_color_at(QtCore.QPointF(2.0, 2.0)) is None
|
||||
|
||||
|
||||
def test_set_pos_center(qapp):
|
||||
item = BeeTextItem('foo bar')
|
||||
with patch.object(item, 'bounding_rect_unselected',
|
||||
|
|
|
|||
|
|
@ -13,18 +13,7 @@ from beeref.items import BeePixmapItem
|
|||
def test_init_selectable(view):
|
||||
item = BeePixmapItem(QtGui.QImage())
|
||||
assert item.viewport_scale == 1
|
||||
assert item.scale_active is False
|
||||
assert item.rotate_active is False
|
||||
assert item.flip_active is False
|
||||
|
||||
|
||||
def test_is_action_active_when_no_action(view, item):
|
||||
assert item.is_action_active() is False
|
||||
|
||||
|
||||
def test_is_action_active_when_action(view, item):
|
||||
item.scale_active = True
|
||||
assert item.is_action_active() is True
|
||||
assert item.active_mode is None
|
||||
|
||||
|
||||
def test_on_view_scale_change(view, item):
|
||||
|
|
@ -835,7 +824,7 @@ def test_mouse_press_event_topleft_scale(view, item):
|
|||
with patch.object(item, 'bounding_rect_unselected',
|
||||
return_value=QtCore.QRectF(0, 0, 100, 80)):
|
||||
item.mousePressEvent(event)
|
||||
assert item.scale_active is True
|
||||
assert item.active_mode == item.SCALE_MODE
|
||||
assert item.event_start == QtCore.QPointF(-1, -1)
|
||||
assert item.event_direction.x() < 0
|
||||
assert item.event_direction.y() < 0
|
||||
|
|
@ -853,7 +842,7 @@ def test_mouse_press_event_bottomright_scale(view, item):
|
|||
with patch.object(item, 'bounding_rect_unselected',
|
||||
return_value=QtCore.QRectF(0, 0, 100, 80)):
|
||||
item.mousePressEvent(event)
|
||||
assert item.scale_active is True
|
||||
assert item.active_mode == item.SCALE_MODE
|
||||
assert item.event_start == QtCore.QPointF(101, 81)
|
||||
assert item.event_direction.x() > 0
|
||||
assert item.event_direction.y() > 0
|
||||
|
|
@ -872,7 +861,7 @@ def test_mouse_press_event_rotate(view, item):
|
|||
return_value=QtCore.QRectF(0, 0, 100, 80)):
|
||||
with patch('PyQt6.QtWidgets.QGraphicsPixmapItem.mousePressEvent'):
|
||||
item.mousePressEvent(event)
|
||||
assert item.rotate_active is True
|
||||
assert item.active_mode == item.ROTATE_MODE
|
||||
assert item.event_anchor == QtCore.QPointF(50, 40)
|
||||
assert item.rotate_orig_degrees == 0
|
||||
event.accept.assert_called_once_with()
|
||||
|
|
@ -895,7 +884,7 @@ def test_mouse_press_event_flip(view, item):
|
|||
assert cmd.items == [item]
|
||||
assert cmd.anchor == QtCore.QPointF(50, 40)
|
||||
assert cmd.vertical is False
|
||||
assert item.flip_active is True
|
||||
assert item.active_mode == item.FLIP_MODE
|
||||
event.accept.assert_called_once_with()
|
||||
|
||||
|
||||
|
|
@ -909,9 +898,7 @@ def test_mouse_press_event_not_in_handles(view, item):
|
|||
with patch('PyQt6.QtWidgets.QGraphicsPixmapItem.mousePressEvent') as m:
|
||||
item.mousePressEvent(event)
|
||||
m.assert_called_once_with(event)
|
||||
assert item.scale_active is False
|
||||
assert item.rotate_active is False
|
||||
assert item.flip_active is False
|
||||
assert item.active_mode is None
|
||||
event.accept.assert_not_called()
|
||||
|
||||
|
||||
|
|
@ -945,7 +932,7 @@ def test_mouse_move_event_when_scale_action(view, item):
|
|||
view.scene.addItem(item)
|
||||
event = MagicMock()
|
||||
event.scenePos.return_value = QtCore.QPointF(20, 90)
|
||||
item.scale_active = True
|
||||
item.active_mode = item.SCALE_MODE
|
||||
item.event_direction = QtCore.QPointF(1, 1) / math.sqrt(2)
|
||||
item.event_anchor = QtCore.QPointF(100, 80)
|
||||
item.event_start = QtCore.QPointF(10, 10)
|
||||
|
|
@ -965,7 +952,7 @@ def test_mouse_move_event_when_rotate_action(view, item):
|
|||
event = MagicMock()
|
||||
event.scenePos.return_value = QtCore.QPointF(15, 25)
|
||||
item.event_start = QtCore.QPointF(10, 10)
|
||||
item.rotate_active = True
|
||||
item.active_mode = item.ROTATE_MODE
|
||||
item.rotate_orig_degrees = 0
|
||||
item.rotate_start_angle = -3
|
||||
item.event_anchor = QtCore.QPointF(10, 20)
|
||||
|
|
@ -981,7 +968,7 @@ def test_mouse_move_event_when_flip_action(view, item):
|
|||
event = MagicMock()
|
||||
event.scenePos.return_value = QtCore.QPointF(15, 25)
|
||||
item.event_start = QtCore.QPointF(10, 10)
|
||||
item.flip_active = True
|
||||
item.active_mode = item.FLIP_MODE
|
||||
with patch('PyQt6.QtWidgets.QGraphicsPixmapItem.mouseMoveEvent') as m:
|
||||
item.mouseMoveEvent(event)
|
||||
m.assert_not_called()
|
||||
|
|
@ -991,13 +978,13 @@ def test_mouse_move_event_when_flip_action(view, item):
|
|||
def test_mouse_release_event_when_no_action(view, item):
|
||||
view.scene.addItem(item)
|
||||
event = MagicMock()
|
||||
item.flip_active = True
|
||||
item.active_mode = item.FLIP_MODE
|
||||
event.pos.return_value = QtCore.QPointF(-100, -100)
|
||||
with patch('PyQt6.QtWidgets.QGraphicsPixmapItem'
|
||||
'.mouseReleaseEvent') as m:
|
||||
item.mouseReleaseEvent(event)
|
||||
m.assert_called_once_with(event)
|
||||
item.flip_active is False
|
||||
item.active_mode is None
|
||||
event.accept.assert_not_called()
|
||||
|
||||
|
||||
|
|
@ -1005,7 +992,7 @@ def test_mouse_release_event_when_scale_action(view, item):
|
|||
view.scene.addItem(item)
|
||||
event = MagicMock()
|
||||
event.scenePos.return_value = QtCore.QPointF(20, 90)
|
||||
item.scale_active = True
|
||||
item.active_mode = item.SCALE_MODE
|
||||
item.event_direction = QtCore.QPointF(1, 1) / math.sqrt(2)
|
||||
item.event_anchor = QtCore.QPointF(100, 80)
|
||||
item.event_start = QtCore.QPointF(10, 10)
|
||||
|
|
@ -1023,7 +1010,7 @@ def test_mouse_release_event_when_scale_action(view, item):
|
|||
assert cmd.factor == approx(1.5, 0.01)
|
||||
assert cmd.anchor == QtCore.QPointF(100, 80)
|
||||
assert cmd.ignore_first_redo is True
|
||||
assert item.scale_active is False
|
||||
assert item.active_mode is None
|
||||
event.accept.assert_called_once_with()
|
||||
|
||||
|
||||
|
|
@ -1031,7 +1018,7 @@ def test_mouse_release_event_when_scale_action_zero(view, item):
|
|||
view.scene.addItem(item)
|
||||
event = MagicMock()
|
||||
event.scenePos.return_value = QtCore.QPointF(20, 90)
|
||||
item.scale_active = True
|
||||
item.active_mode = item.SCALE_MODE
|
||||
item.event_direction = QtCore.QPointF(1, 1) / math.sqrt(2)
|
||||
item.event_anchor = QtCore.QPointF(100, 80)
|
||||
item.event_start = QtCore.QPointF(20, 90)
|
||||
|
|
@ -1042,7 +1029,7 @@ def test_mouse_release_event_when_scale_action_zero(view, item):
|
|||
return_value=QtCore.QRectF(0, 0, 100, 80)):
|
||||
item.mouseReleaseEvent(event)
|
||||
view.scene.undo_stack.push.assert_not_called()
|
||||
assert item.scale_active is False
|
||||
assert item.active_mode is None
|
||||
event.accept.assert_called_once_with()
|
||||
|
||||
|
||||
|
|
@ -1050,7 +1037,7 @@ def test_mouse_release_event_when_rotate_action(view, item):
|
|||
view.scene.addItem(item)
|
||||
event = MagicMock()
|
||||
event.scenePos.return_value = QtCore.QPointF(15, 25)
|
||||
item.rotate_active = True
|
||||
item.active_mode = item.ROTATE_MODE
|
||||
item.rotate_orig_degrees = 0
|
||||
item.rotate_start_angle = -3
|
||||
item.event_anchor = QtCore.QPointF(10, 20)
|
||||
|
|
@ -1065,7 +1052,7 @@ def test_mouse_release_event_when_rotate_action(view, item):
|
|||
assert cmd.delta == -42
|
||||
assert cmd.anchor == QtCore.QPointF(10, 20)
|
||||
assert cmd.ignore_first_redo is True
|
||||
assert item.rotate_active is False
|
||||
assert item.active_mode is None
|
||||
event.accept.assert_called_once_with()
|
||||
|
||||
|
||||
|
|
@ -1073,7 +1060,7 @@ def test_mouse_release_event_when_rotate_action_zero(view, item):
|
|||
view.scene.addItem(item)
|
||||
event = MagicMock()
|
||||
event.scenePos.return_value = QtCore.QPointF(15, 25)
|
||||
item.rotate_active = True
|
||||
item.active_mode = item.ROTATE_MODE
|
||||
item.rotate_orig_degrees = 0
|
||||
item.rotate_start_angle = -45
|
||||
item.event_anchor = QtCore.QPointF(10, 20)
|
||||
|
|
@ -1081,7 +1068,7 @@ def test_mouse_release_event_when_rotate_action_zero(view, item):
|
|||
|
||||
item.mouseReleaseEvent(event)
|
||||
view.scene.undo_stack.push.assert_not_called()
|
||||
assert item.rotate_active is False
|
||||
assert item.active_mode is None
|
||||
event.accept.assert_called_once_with()
|
||||
|
||||
|
||||
|
|
@ -1089,12 +1076,12 @@ def test_mouse_release_event_when_flip_action(view, item):
|
|||
view.scene.addItem(item)
|
||||
event = MagicMock()
|
||||
event.pos.return_value = QtCore.QPointF(0, 40)
|
||||
item.flip_active = True
|
||||
item.active_mode = item.FLIP_MODE
|
||||
view.scene.undo_stack = MagicMock(push=MagicMock())
|
||||
|
||||
with patch.object(item, 'bounding_rect_unselected',
|
||||
return_value=QtCore.QRectF(0, 0, 100, 80)):
|
||||
item.mouseReleaseEvent(event)
|
||||
view.scene.undo_stack.push.assert_not_called()
|
||||
assert item.flip_active is False
|
||||
assert item.active_mode is None
|
||||
event.accept.assert_called_once_with()
|
||||
|
|
|
|||
|
|
@ -495,6 +495,25 @@ def test_crop_item_when_not_image(view):
|
|||
item.enter_crop_mode.assert_not_called()
|
||||
|
||||
|
||||
def test_sample_color_at_when_pixmap_item(view):
|
||||
color = QtGui.QColor(255, 0, 0, 3)
|
||||
img = QtGui.QImage(10, 10, QtGui.QImage.Format.Format_ARGB32)
|
||||
img.fill(color)
|
||||
item = BeePixmapItem(img, 'foo.png')
|
||||
view.scene.addItem(item)
|
||||
assert view.scene.sample_color_at(QtCore.QPointF(2, 2)) == color
|
||||
|
||||
|
||||
def test_sample_color_at_when_text_item(view):
|
||||
item = BeeTextItem('foo bar baz')
|
||||
view.scene.addItem(item)
|
||||
assert view.scene.sample_color_at(QtCore.QPointF(2, 2)) is None
|
||||
|
||||
|
||||
def test_sample_color_at_when_no_item(view):
|
||||
assert view.scene.sample_color_at(QtCore.QPointF(2, 2)) is None
|
||||
|
||||
|
||||
def test_select_all_items_when_true(view):
|
||||
item1 = BeeTextItem('foo')
|
||||
view.scene.addItem(item1)
|
||||
|
|
@ -631,8 +650,7 @@ def test_mouse_press_event_when_left_click_over_item(mouse_mock, view, item):
|
|||
view.scene.mousePressEvent(event)
|
||||
event.accept.assert_not_called()
|
||||
mouse_mock.assert_called_once_with(event)
|
||||
assert view.scene.move_active is True
|
||||
assert view.scene.rubberband_active is False
|
||||
assert view.scene.active_mode == view.scene.MOVE_MODE
|
||||
assert view.scene.event_start == QtCore.QPointF(10, 20)
|
||||
|
||||
|
||||
|
|
@ -651,8 +669,7 @@ def test_mouse_press_event_when_left_click_over_item_in_edit_mode(
|
|||
event.accept.assert_not_called()
|
||||
mouse_mock.assert_called_once_with(event)
|
||||
item.exit_edit_mode.assert_not_called()
|
||||
assert view.scene.move_active is False
|
||||
assert view.scene.rubberband_active is False
|
||||
assert view.scene.active_mode is None
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QGraphicsScene.mousePressEvent')
|
||||
|
|
@ -670,8 +687,7 @@ def test_mouse_press_event_when_left_click_over_diff_item_in_edit_mode(
|
|||
event.accept.assert_not_called()
|
||||
mouse_mock.assert_called_once_with(event)
|
||||
txtitem.exit_edit_mode.assert_called_once_with()
|
||||
assert view.scene.move_active is True
|
||||
assert view.scene.rubberband_active is False
|
||||
assert view.scene.active_mode == view.scene.MOVE_MODE
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QGraphicsScene.mousePressEvent')
|
||||
|
|
@ -689,8 +705,7 @@ def test_mouse_press_event_when_left_click_over_no_item_in_edit_mode(
|
|||
event.accept.assert_not_called()
|
||||
mouse_mock.assert_called_once_with(event)
|
||||
item.exit_edit_mode.assert_called_once_with()
|
||||
assert view.scene.move_active is False
|
||||
assert view.scene.rubberband_active is True
|
||||
assert view.scene.active_mode == view.scene.RUBBERBAND_MODE
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QGraphicsScene.mousePressEvent')
|
||||
|
|
@ -707,8 +722,7 @@ def test_mouse_press_event_when_left_click_over_item_in_crop_mode(
|
|||
event.accept.assert_not_called()
|
||||
mouse_mock.assert_called_once_with(event)
|
||||
view.scene.cancel_crop_mode.assert_not_called()
|
||||
assert view.scene.move_active is False
|
||||
assert view.scene.rubberband_active is False
|
||||
assert view.scene.active_mode is None
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QGraphicsScene.mousePressEvent')
|
||||
|
|
@ -726,8 +740,7 @@ def test_mouse_press_event_when_left_click_over_diff_item_in_crop_mode(
|
|||
event.accept.assert_not_called()
|
||||
mouse_mock.assert_called_once_with(event)
|
||||
view.scene.cancel_crop_mode.assert_called_once_with()
|
||||
assert view.scene.move_active is True
|
||||
assert view.scene.rubberband_active is False
|
||||
assert view.scene.active_mode is view.scene.MOVE_MODE
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QGraphicsScene.mousePressEvent')
|
||||
|
|
@ -744,8 +757,7 @@ def test_mouse_press_event_when_left_click_over_no_item_in_crop_mode(
|
|||
event.accept.assert_not_called()
|
||||
mouse_mock.assert_called_once_with(event)
|
||||
view.scene.cancel_crop_mode.assert_called_once_with()
|
||||
assert view.scene.move_active is False
|
||||
assert view.scene.rubberband_active is True
|
||||
assert view.scene.active_mode == view.scene.RUBBERBAND_MODE
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QGraphicsScene.mousePressEvent')
|
||||
|
|
@ -760,8 +772,7 @@ def test_mouse_press_event_when_left_click_not_over_item(
|
|||
view.scene.mousePressEvent(event)
|
||||
event.accept.assert_not_called()
|
||||
mouse_mock.assert_called_once_with(event)
|
||||
assert view.scene.move_active is False
|
||||
assert view.scene.rubberband_active is True
|
||||
assert view.scene.active_mode == view.scene.RUBBERBAND_MODE
|
||||
assert view.scene.event_start == QtCore.QPointF(10, 20)
|
||||
|
||||
|
||||
|
|
@ -775,15 +786,14 @@ def test_mouse_press_event_when_no_items(mouse_mock, view):
|
|||
view.scene.mousePressEvent(event)
|
||||
event.accept.assert_not_called()
|
||||
mouse_mock.assert_called_once_with(event)
|
||||
assert view.scene.move_active is False
|
||||
assert view.scene.rubberband_active is False
|
||||
assert view.scene.active_mode is None
|
||||
mouse_mock.assert_called_once_with(event)
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QGraphicsScene.mouseDoubleClickEvent')
|
||||
def test_mouse_doubleclick_event_when_over_item(mouse_mock, view, item):
|
||||
event = MagicMock()
|
||||
view.scene.move_active = True
|
||||
view.scene.active_mode = view.scene.MOVE_MODE
|
||||
view.scene.addItem(item)
|
||||
item.setPos(30, 40)
|
||||
item.setSelected(True)
|
||||
|
|
@ -794,7 +804,7 @@ def test_mouse_doubleclick_event_when_over_item(mouse_mock, view, item):
|
|||
return_value=QtCore.QRectF(0, 0, 100, 100)):
|
||||
view.scene.mouseDoubleClickEvent(event)
|
||||
|
||||
assert view.scene.move_active is False
|
||||
assert view.scene.active_mode is None
|
||||
view.fit_rect.assert_called_once_with(
|
||||
QtCore.QRectF(30, 40, 100, 100), toggle_item=item)
|
||||
mouse_mock.assert_not_called()
|
||||
|
|
@ -807,7 +817,7 @@ def test_mouse_doubleclick_event_when_over_editable_item(
|
|||
item = BeeTextItem('foo bar')
|
||||
item.enter_edit_mode = MagicMock()
|
||||
event = MagicMock()
|
||||
view.scene.move_active = True
|
||||
view.scene.active_mode = view.scene.MOVE_MODE
|
||||
view.scene.addItem(item)
|
||||
item.setPos(30, 40)
|
||||
item.setSelected(True)
|
||||
|
|
@ -818,7 +828,7 @@ def test_mouse_doubleclick_event_when_over_editable_item(
|
|||
return_value=QtCore.QRectF(0, 0, 100, 100)):
|
||||
view.scene.mouseDoubleClickEvent(event)
|
||||
|
||||
assert view.scene.move_active is False
|
||||
assert view.scene.active_mode is None
|
||||
item.enter_edit_mode.assert_called_once_with()
|
||||
double_mock.assert_not_called()
|
||||
press_mock.assert_called_once_with(event)
|
||||
|
|
@ -828,7 +838,7 @@ def test_mouse_doubleclick_event_when_over_editable_item(
|
|||
def test_mouse_doubleclick_event_when_item_not_selected(
|
||||
mouse_mock, view, item):
|
||||
event = MagicMock()
|
||||
view.scene.move_active = True
|
||||
view.scene.active_mode = view.scene.MOVE_MODE
|
||||
view.scene.addItem(item)
|
||||
item.setPos(30, 40)
|
||||
item.setSelected(False)
|
||||
|
|
@ -839,7 +849,7 @@ def test_mouse_doubleclick_event_when_item_not_selected(
|
|||
return_value=QtCore.QRectF(0, 0, 100, 100)):
|
||||
view.scene.mouseDoubleClickEvent(event)
|
||||
|
||||
assert view.scene.move_active is False
|
||||
assert view.scene.active_mode is None
|
||||
view.fit_rect.assert_called_once_with(
|
||||
QtCore.QRectF(30, 40, 100, 100), toggle_item=item)
|
||||
mouse_mock.assert_not_called()
|
||||
|
|
@ -861,7 +871,7 @@ def test_mouse_move_event_when_rubberband_new(
|
|||
mouse_mock, view, imgfilename3x3):
|
||||
item = BeePixmapItem(QtGui.QImage(imgfilename3x3))
|
||||
view.scene.addItem(item)
|
||||
view.scene.rubberband_active = True
|
||||
view.scene.active_mode = view.scene.RUBBERBAND_MODE
|
||||
view.scene.addItem = MagicMock()
|
||||
view.scene.event_start = QtCore.QPointF(0, 0)
|
||||
view.scene.rubberband_item.bring_to_front = MagicMock()
|
||||
|
|
@ -885,7 +895,7 @@ def test_mouse_move_event_when_rubberband_not_new(
|
|||
mouse_mock, view, imgfilename3x3):
|
||||
item = BeePixmapItem(QtGui.QImage(imgfilename3x3))
|
||||
view.scene.addItem(item)
|
||||
view.scene.rubberband_active = True
|
||||
view.scene.active_mode = view.scene.RUBBERBAND_MODE
|
||||
view.scene.event_start = QtCore.QPointF(0, 0)
|
||||
view.scene.rubberband_item.bring_to_front = MagicMock()
|
||||
view.scene.addItem(view.scene.rubberband_item)
|
||||
|
|
@ -908,7 +918,7 @@ def test_mouse_move_event_when_rubberband_not_new(
|
|||
def test_mouse_move_event_when_no_rubberband(mouse_mock, view, imgfilename3x3):
|
||||
item = BeePixmapItem(QtGui.QImage(imgfilename3x3))
|
||||
view.scene.addItem(item)
|
||||
view.scene.rubberband_active = False
|
||||
view.scene.active_mode = None
|
||||
view.scene.event_start = QtCore.QPointF(0, 0)
|
||||
view.scene.rubberband_item.bring_to_front = MagicMock()
|
||||
view.scene.addItem = MagicMock()
|
||||
|
|
@ -929,13 +939,13 @@ def test_mouse_move_event_when_no_rubberband(mouse_mock, view, imgfilename3x3):
|
|||
@patch('PyQt6.QtWidgets.QGraphicsScene.mouseReleaseEvent')
|
||||
def test_mouse_release_event_when_rubberband_active(mouse_mock, view):
|
||||
event = MagicMock()
|
||||
view.scene.rubberband_active = True
|
||||
view.scene.active_mode = view.scene.RUBBERBAND_MODE
|
||||
view.scene.addItem(view.scene.rubberband_item)
|
||||
view.scene.removeItem = MagicMock()
|
||||
|
||||
view.scene.mouseReleaseEvent(event)
|
||||
view.scene.removeItem.assert_called_once_with(view.scene.rubberband_item)
|
||||
view.scene.rubberband_active is False
|
||||
view.scene.active_mode is None
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QGraphicsScene.mouseReleaseEvent')
|
||||
|
|
@ -943,7 +953,7 @@ def test_mouse_release_event_when_move_active(mouse_mock, view, item):
|
|||
view.scene.addItem(item)
|
||||
item.setSelected(True)
|
||||
event = MagicMock(scenePos=MagicMock(return_value=QtCore.QPoint(10, 20)))
|
||||
view.scene.move_active = True
|
||||
view.scene.active_mode = view.scene.MOVE_MODE
|
||||
view.scene.event_start = QtCore.QPoint(0, 0)
|
||||
view.scene.undo_stack = MagicMock(push=MagicMock())
|
||||
|
||||
|
|
@ -957,7 +967,7 @@ def test_mouse_release_event_when_move_active(mouse_mock, view, item):
|
|||
assert cmd.delta.x() == 10
|
||||
assert cmd.delta.y() == 20
|
||||
mouse_mock.assert_called_once_with(event)
|
||||
assert view.scene.move_active is False
|
||||
assert view.scene.active_mode is None
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QGraphicsScene.mouseReleaseEvent')
|
||||
|
|
@ -965,13 +975,13 @@ def test_mouse_release_event_when_move_not_active(mouse_mock, view, item):
|
|||
view.scene.addItem(item)
|
||||
item.setSelected(True)
|
||||
event = MagicMock(scenePos=MagicMock(return_value=QtCore.QPoint(10, 20)))
|
||||
view.scene.move_active = False
|
||||
view.scene.active_mode = None
|
||||
view.scene.undo_stack = MagicMock(push=MagicMock())
|
||||
|
||||
view.scene.mouseReleaseEvent(event)
|
||||
view.scene.undo_stack.push.assert_not_called()
|
||||
mouse_mock.assert_called_once_with(event)
|
||||
assert view.scene.move_active is False
|
||||
assert view.scene.active_mode is None
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QGraphicsScene.mouseReleaseEvent')
|
||||
|
|
@ -979,13 +989,13 @@ def test_mouse_release_event_when_no_selection(mouse_mock, view, item):
|
|||
view.scene.addItem(item)
|
||||
item.setSelected(False)
|
||||
event = MagicMock(scenePos=MagicMock(return_value=QtCore.QPoint(10, 20)))
|
||||
view.scene.move_active = True
|
||||
view.scene.active_mode = view.scene.MOVE_MODE
|
||||
view.scene.undo_stack = MagicMock(push=MagicMock())
|
||||
|
||||
view.scene.mouseReleaseEvent(event)
|
||||
view.scene.undo_stack.push.assert_not_called()
|
||||
mouse_mock.assert_called_once_with(event)
|
||||
assert view.scene.move_active is False
|
||||
assert view.scene.active_mode is None
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QGraphicsScene.mouseReleaseEvent')
|
||||
|
|
@ -993,14 +1003,14 @@ def test_mouse_release_event_when_item_action_active(mouse_mock, view, item):
|
|||
view.scene.addItem(item)
|
||||
item.setSelected(True)
|
||||
event = MagicMock(scenePos=MagicMock(return_value=QtCore.QPoint(10, 20)))
|
||||
view.scene.move_active = True
|
||||
item.scale_active = True
|
||||
item.active_mode = item.SCALE_MODE
|
||||
view.scene.active_mode = view.scene.MOVE_MODE
|
||||
view.scene.undo_stack = MagicMock(push=MagicMock())
|
||||
|
||||
view.scene.mouseReleaseEvent(event)
|
||||
view.scene.undo_stack.push.assert_not_called()
|
||||
mouse_mock.assert_called_once_with(event)
|
||||
assert view.scene.move_active is False
|
||||
assert view.scene.active_mode is None
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QGraphicsScene.mouseReleaseEvent')
|
||||
|
|
@ -1012,14 +1022,14 @@ def test_mouse_release_event_when_multiselect_action_active(mouse_mock, view):
|
|||
view.scene.addItem(item2)
|
||||
item2.setSelected(True)
|
||||
event = MagicMock(scenePos=MagicMock(return_value=QtCore.QPoint(10, 20)))
|
||||
view.scene.move_active = True
|
||||
view.scene.multi_select_item.scale_active = True
|
||||
view.scene.active_mode = view.scene.MOVE_MODE
|
||||
view.scene.multi_select_item.active_mode = BeePixmapItem.SCALE_MODE
|
||||
view.scene.undo_stack = MagicMock(push=MagicMock())
|
||||
|
||||
view.scene.mouseReleaseEvent(event)
|
||||
view.scene.undo_stack.push.assert_not_called()
|
||||
mouse_mock.assert_called_once_with(event)
|
||||
assert view.scene.move_active is False
|
||||
assert view.scene.active_mode is None
|
||||
|
||||
|
||||
def test_selected_items(view):
|
||||
|
|
@ -1229,8 +1239,7 @@ def test_on_selection_change_when_multi_selection_ended(view):
|
|||
def test_on_change_when_multi_select_when_no_scale_no_rotate(view):
|
||||
view.scene.addItem(view.scene.multi_select_item)
|
||||
view.scene.multi_select_item.fit_selection_area = MagicMock()
|
||||
view.scene.multi_select_item.scale_active = False
|
||||
view.scene.multi_select_item.rotate_active = False
|
||||
view.scene.multi_select_item.active_mode = None
|
||||
view.scene.on_change(None)
|
||||
view.scene.multi_select_item.fit_selection_area.assert_called_once()
|
||||
|
||||
|
|
@ -1238,8 +1247,7 @@ def test_on_change_when_multi_select_when_no_scale_no_rotate(view):
|
|||
def test_on_change_when_multi_select_when_scale_active(view):
|
||||
view.scene.addItem(view.scene.multi_select_item)
|
||||
view.scene.multi_select_item.fit_selection_area = MagicMock()
|
||||
view.scene.multi_select_item.scale_active = True
|
||||
view.scene.multi_select_item.rotate_active = False
|
||||
view.scene.multi_select_item.active_mode = BeePixmapItem.SCALE_MODE
|
||||
view.scene.on_change(None)
|
||||
view.scene.multi_select_item.fit_selection_area.assert_not_called()
|
||||
|
||||
|
|
@ -1247,16 +1255,14 @@ def test_on_change_when_multi_select_when_scale_active(view):
|
|||
def test_on_change_when_multi_select_when_rotate_active(view):
|
||||
view.scene.addItem(view.scene.multi_select_item)
|
||||
view.scene.multi_select_item.fit_selection_area = MagicMock()
|
||||
view.scene.multi_select_item.scale_active = False
|
||||
view.scene.multi_select_item.rotate_active = True
|
||||
view.scene.multi_select_item.active_mode = BeePixmapItem.ROTATE_MODE
|
||||
view.scene.on_change(None)
|
||||
view.scene.multi_select_item.fit_selection_area.assert_not_called()
|
||||
|
||||
|
||||
def test_on_change_when_no_multi_select(view):
|
||||
view.scene.multi_select_item.fit_selection_area = MagicMock()
|
||||
view.scene.multi_select_item.scale_active = True
|
||||
view.scene.multi_select_item.rotate_active = True
|
||||
view.scene.multi_select_item.active_mode = BeePixmapItem.SCALE_MODE
|
||||
view.scene.on_change(None)
|
||||
view.scene.multi_select_item.fit_selection_area.assert_not_called()
|
||||
|
||||
|
|
|
|||
|
|
@ -71,3 +71,10 @@ def test_round_to(number, base, expected):
|
|||
('JPEG (*.jpg *.jpeg)', 'jpg')])
|
||||
def test_get_file_extension_from_format(formatstr, expected):
|
||||
assert utils.get_file_extension_from_format(formatstr) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize('rgba,expected',
|
||||
[((255, 0, 0, 255), '#ff0000'),
|
||||
((255, 0, 0, 100), '#ff000064')])
|
||||
def test_qcolor_to_hex(rgba, expected):
|
||||
assert utils.qcolor_to_hex(QtGui.QColor(*rgba)) == expected
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from unittest.mock import MagicMock, patch, mock_open
|
|||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PyQt6.QtCore import Qt
|
||||
|
||||
from beeref import widgets
|
||||
from beeref.actions import actions
|
||||
from beeref.config import logfile_name
|
||||
from beeref.items import BeePixmapItem, BeeTextItem
|
||||
|
|
@ -185,7 +186,7 @@ def test_on_action_open(dialog_mock, view, qtbot):
|
|||
filename = os.path.join(root, 'assets', 'test1item.bee')
|
||||
dialog_mock.return_value = (filename, None)
|
||||
view.on_loading_finished = MagicMock()
|
||||
view.scene.cancel_crop_mode = MagicMock()
|
||||
view.cancel_active_modes = MagicMock()
|
||||
|
||||
view.on_action_open()
|
||||
qtbot.waitUntil(lambda: view.on_loading_finished.called is True)
|
||||
|
|
@ -194,31 +195,31 @@ def test_on_action_open(dialog_mock, view, qtbot):
|
|||
assert item.isSelected() is False
|
||||
assert item.pixmap()
|
||||
view.on_loading_finished.assert_called_once_with(filename, [])
|
||||
view.scene.cancel_crop_mode.assert_called_once_with()
|
||||
view.cancel_active_modes.assert_called_with()
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QFileDialog.getOpenFileName')
|
||||
@patch('beeref.view.BeeGraphicsView.open_from_file')
|
||||
def test_on_action_open_when_no_filename(open_mock, dialog_mock, view):
|
||||
dialog_mock.return_value = (None, None)
|
||||
view.scene.cancel_crop_mode = MagicMock()
|
||||
view.cancel_active_modes = MagicMock()
|
||||
view.on_action_open()
|
||||
open_mock.assert_not_called()
|
||||
view.scene.cancel_crop_mode.assert_called_once_with()
|
||||
view.cancel_active_modes.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QFileDialog.getSaveFileName')
|
||||
def test_on_action_save_as(dialog_mock, view, imgfilename3x3, tmpdir):
|
||||
item = BeePixmapItem(QtGui.QImage(imgfilename3x3))
|
||||
view.scene.addItem(item)
|
||||
view.scene.cancel_crop_mode = MagicMock()
|
||||
view.cancel_active_modes = MagicMock()
|
||||
filename = os.path.join(tmpdir, 'test.bee')
|
||||
assert os.path.exists(filename) is False
|
||||
dialog_mock.return_value = (filename, None)
|
||||
view.on_action_save_as()
|
||||
view.worker.wait()
|
||||
assert os.path.exists(filename) is True
|
||||
view.scene.cancel_crop_mode.assert_called_once_with()
|
||||
view.cancel_active_modes.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QFileDialog.getSaveFileName')
|
||||
|
|
@ -227,11 +228,11 @@ def test_on_action_save_as_when_no_filename(
|
|||
save_mock, dialog_mock, view, imgfilename3x3):
|
||||
item = BeePixmapItem(QtGui.QImage(imgfilename3x3))
|
||||
view.scene.addItem(item)
|
||||
view.scene.cancel_crop_mode = MagicMock()
|
||||
view.cancel_active_modes = MagicMock()
|
||||
dialog_mock.return_value = (None, None)
|
||||
view.on_action_save_as()
|
||||
save_mock.assert_not_called()
|
||||
view.scene.cancel_crop_mode.assert_called_once_with()
|
||||
view.cancel_active_modes.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QFileDialog.getSaveFileName')
|
||||
|
|
@ -239,7 +240,7 @@ def test_on_action_save_as_filename_doesnt_end_with_bee(
|
|||
dialog_mock, view, qtbot, imgfilename3x3, tmpdir):
|
||||
item = BeePixmapItem(QtGui.QImage(imgfilename3x3))
|
||||
view.scene.addItem(item)
|
||||
view.scene.cancel_crop_mode = MagicMock()
|
||||
view.cancel_active_modes = MagicMock()
|
||||
view.on_saving_finished = MagicMock()
|
||||
filename = os.path.join(tmpdir, 'test')
|
||||
assert os.path.exists(filename) is False
|
||||
|
|
@ -248,7 +249,7 @@ def test_on_action_save_as_filename_doesnt_end_with_bee(
|
|||
qtbot.waitUntil(lambda: view.on_saving_finished.called is True)
|
||||
assert os.path.exists(f'{filename}.bee') is True
|
||||
view.on_saving_finished.assert_called_once_with(f'{filename}.bee', [])
|
||||
view.scene.cancel_crop_mode.assert_called_once_with()
|
||||
view.cancel_active_modes.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QFileDialog.getSaveFileName')
|
||||
|
|
@ -258,20 +259,20 @@ def test_on_action_save_as_when_error(
|
|||
item = BeePixmapItem(QtGui.QImage(imgfilename3x3))
|
||||
view.scene.addItem(item)
|
||||
view.on_saving_finished = MagicMock()
|
||||
view.scene.cancel_crop_mode = MagicMock()
|
||||
view.cancel_active_modes = MagicMock()
|
||||
filename = os.path.join(tmpdir, 'test.bee')
|
||||
dialog_mock.return_value = (filename, None)
|
||||
save_mock.side_effect = sqlite3.Error('foo')
|
||||
view.on_action_save_as()
|
||||
qtbot.waitUntil(lambda: view.on_saving_finished.called is True)
|
||||
view.on_saving_finished.assert_called_once_with(filename, ['foo'])
|
||||
view.scene.cancel_crop_mode.assert_called_once_with()
|
||||
view.cancel_active_modes.assert_called_once_with()
|
||||
|
||||
|
||||
def test_on_action_save(view, qtbot, imgfilename3x3, tmpdir):
|
||||
item = BeePixmapItem(QtGui.QImage(imgfilename3x3))
|
||||
view.scene.addItem(item)
|
||||
view.scene.cancel_crop_mode = MagicMock()
|
||||
view.cancel_active_modes = MagicMock()
|
||||
view.filename = os.path.join(tmpdir, 'test.bee')
|
||||
root = os.path.dirname(__file__)
|
||||
shutil.copyfile(os.path.join(root, 'assets', 'test1item.bee'),
|
||||
|
|
@ -281,18 +282,18 @@ def test_on_action_save(view, qtbot, imgfilename3x3, tmpdir):
|
|||
qtbot.waitUntil(lambda: view.on_saving_finished.called is True)
|
||||
assert os.path.exists(view.filename) is True
|
||||
view.on_saving_finished.assert_called_once_with(view.filename, [])
|
||||
view.scene.cancel_crop_mode.assert_called_once_with()
|
||||
view.cancel_active_modes.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('beeref.view.BeeGraphicsView.on_action_save_as')
|
||||
def test_on_action_save_when_no_filename(save_as_mock, view, imgfilename3x3):
|
||||
item = BeePixmapItem(QtGui.QImage(imgfilename3x3))
|
||||
view.scene.addItem(item)
|
||||
view.scene.cancel_crop_mode = MagicMock()
|
||||
view.cancel_active_modes = MagicMock()
|
||||
view.filename = None
|
||||
view.on_action_save()
|
||||
save_as_mock.assert_called_once_with()
|
||||
view.scene.cancel_crop_mode.assert_called_once_with()
|
||||
view.cancel_active_modes.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('beeref.widgets.SceneToPixmapExporterDialog.exec')
|
||||
|
|
@ -401,7 +402,7 @@ def test_on_action_insert_images_new_scene(
|
|||
dialog_mock, clear_mock, view, imgfilename3x3, qtbot):
|
||||
dialog_mock.return_value = ([imgfilename3x3], None)
|
||||
view.on_insert_images_finished = MagicMock()
|
||||
view.scene.cancel_crop_mode = MagicMock()
|
||||
view.cancel_active_modes = MagicMock()
|
||||
view.on_action_insert_images()
|
||||
qtbot.waitUntil(lambda: view.on_insert_images_finished.called is True)
|
||||
assert len(view.scene.items()) == 1
|
||||
|
|
@ -410,7 +411,7 @@ def test_on_action_insert_images_new_scene(
|
|||
assert item.pixmap()
|
||||
clear_mock.assert_called_once_with()
|
||||
view.on_insert_images_finished.assert_called_once_with(True, '', [])
|
||||
view.scene.cancel_crop_mode.assert_called_once_with()
|
||||
view.cancel_active_modes.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('beeref.scene.BeeGraphicsScene.clearSelection')
|
||||
|
|
@ -420,7 +421,7 @@ def test_on_action_insert_images_existing_scene(
|
|||
view.scene.addItem(item)
|
||||
dialog_mock.return_value = ([imgfilename3x3], None)
|
||||
view.on_insert_images_finished = MagicMock()
|
||||
view.scene.cancel_crop_mode = MagicMock()
|
||||
view.cancel_active_modes = MagicMock()
|
||||
view.on_action_insert_images()
|
||||
qtbot.waitUntil(lambda: view.on_insert_images_finished.called is True)
|
||||
assert len(view.scene.items()) == 2
|
||||
|
|
@ -429,7 +430,7 @@ def test_on_action_insert_images_existing_scene(
|
|||
assert item.pixmap()
|
||||
clear_mock.assert_called_once_with()
|
||||
view.on_insert_images_finished.assert_called_once_with(False, '', [])
|
||||
view.scene.cancel_crop_mode.assert_called_once_with()
|
||||
view.cancel_active_modes.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('beeref.scene.BeeGraphicsScene.clearSelection')
|
||||
|
|
@ -438,7 +439,7 @@ def test_on_action_insert_images_when_error(
|
|||
dialog_mock, clear_mock, view, imgfilename3x3, qtbot):
|
||||
dialog_mock.return_value = ([imgfilename3x3, 'iaeiae', 'trntrn'], None)
|
||||
view.on_insert_images_finished = MagicMock()
|
||||
view.scene.cancel_crop_mode = MagicMock()
|
||||
view.cancel_active_modes = MagicMock()
|
||||
view.on_action_insert_images()
|
||||
qtbot.waitUntil(lambda: view.on_insert_images_finished.called is True)
|
||||
assert len(view.scene.items()) == 1
|
||||
|
|
@ -448,26 +449,26 @@ def test_on_action_insert_images_when_error(
|
|||
clear_mock.assert_called_once_with()
|
||||
view.on_insert_images_finished.assert_called_once_with(
|
||||
True, '', ['iaeiae', 'trntrn'])
|
||||
view.scene.cancel_crop_mode.assert_called_once_with()
|
||||
view.cancel_active_modes.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('beeref.scene.BeeGraphicsScene.clearSelection')
|
||||
def test_on_action_insert_text(clear_mock, view):
|
||||
view.scene.cancel_crop_mode = MagicMock()
|
||||
view.cancel_active_modes = MagicMock()
|
||||
view.on_action_insert_text()
|
||||
clear_mock.assert_called_once_with()
|
||||
assert len(view.scene.items()) == 1
|
||||
item = view.scene.items()[0]
|
||||
assert item.toPlainText() == 'Text'
|
||||
assert item.isSelected() is True
|
||||
view.scene.cancel_crop_mode.assert_called_once_with()
|
||||
view.cancel_active_modes.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QApplication.clipboard')
|
||||
def test_on_action_copy_image(clipboard_mock, view, imgfilename3x3):
|
||||
item = BeePixmapItem(QtGui.QImage(imgfilename3x3))
|
||||
view.scene.addItem(item)
|
||||
view.scene.cancel_crop_mode = MagicMock()
|
||||
view.cancel_active_modes = MagicMock()
|
||||
item.setSelected(True)
|
||||
mimedata = QtCore.QMimeData()
|
||||
clipboard_mock.return_value.mimeData.return_value = mimedata
|
||||
|
|
@ -476,14 +477,14 @@ def test_on_action_copy_image(clipboard_mock, view, imgfilename3x3):
|
|||
clipboard_mock.return_value.setPixmap.assert_called_once()
|
||||
view.scene.internal_clipboard == [item]
|
||||
assert mimedata.data('beeref/items') == b'1'
|
||||
view.scene.cancel_crop_mode.assert_called_once_with()
|
||||
view.cancel_active_modes.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QApplication.clipboard')
|
||||
def test_on_action_copy_text(clipboard_mock, view, imgfilename3x3):
|
||||
item = BeeTextItem('foo bar')
|
||||
view.scene.addItem(item)
|
||||
view.scene.cancel_crop_mode = MagicMock()
|
||||
view.cancel_active_modes = MagicMock()
|
||||
item.setSelected(True)
|
||||
mimedata = QtCore.QMimeData()
|
||||
clipboard_mock.return_value.mimeData.return_value = mimedata
|
||||
|
|
@ -492,7 +493,7 @@ def test_on_action_copy_text(clipboard_mock, view, imgfilename3x3):
|
|||
clipboard_mock.return_value.setText.assert_called_once_with('foo bar')
|
||||
view.scene.internal_clipboard == [item]
|
||||
assert mimedata.data('beeref/items') == b'1'
|
||||
view.scene.cancel_crop_mode.assert_called_once_with()
|
||||
view.cancel_active_modes.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('beeref.view.BeeGraphicsView.on_action_fit_scene')
|
||||
|
|
@ -501,12 +502,12 @@ def test_on_action_copy_text(clipboard_mock, view, imgfilename3x3):
|
|||
def test_on_action_paste_external_new_scene(
|
||||
clipboard_mock, clear_mock, fit_mock, view, imgfilename3x3):
|
||||
clipboard_mock.return_value = QtGui.QImage(imgfilename3x3)
|
||||
view.scene.cancel_crop_mode = MagicMock()
|
||||
view.cancel_active_modes = MagicMock()
|
||||
view.on_action_paste()
|
||||
assert len(view.scene.items()) == 1
|
||||
assert view.scene.items()[0].isSelected() is True
|
||||
fit_mock.assert_called_once_with()
|
||||
view.scene.cancel_crop_mode.assert_called_once_with()
|
||||
view.cancel_active_modes.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('beeref.view.BeeGraphicsView.on_action_fit_scene')
|
||||
|
|
@ -515,14 +516,14 @@ def test_on_action_paste_external_new_scene(
|
|||
def test_on_action_paste_external_existing_scene(
|
||||
clipboard_mock, clear_mock, fit_mock, view, item, imgfilename3x3):
|
||||
view.scene.addItem(item)
|
||||
view.scene.cancel_crop_mode = MagicMock()
|
||||
view.cancel_active_modes = MagicMock()
|
||||
clipboard_mock.return_value = QtGui.QImage(imgfilename3x3)
|
||||
view.on_action_paste()
|
||||
assert len(view.scene.items()) == 2
|
||||
assert view.scene.items()[0].isSelected() is True
|
||||
assert view.scene.items()[1].isSelected() is False
|
||||
fit_mock.assert_not_called()
|
||||
view.scene.cancel_crop_mode.assert_called_once_with()
|
||||
view.cancel_active_modes.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('beeref.scene.BeeGraphicsScene.clearSelection')
|
||||
|
|
@ -533,12 +534,12 @@ def test_on_action_paste_internal(mimedata_mock, clear_mock, view):
|
|||
mimedata_mock.return_value = mimedata
|
||||
item = BeePixmapItem(QtGui.QImage())
|
||||
view.scene.internal_clipboard = [item]
|
||||
view.scene.cancel_crop_mode = MagicMock()
|
||||
view.cancel_active_modes = MagicMock()
|
||||
view.on_action_paste()
|
||||
assert len(view.scene.items()) == 1
|
||||
assert view.scene.items()[0].isSelected() is True
|
||||
clear_mock.assert_called_once_with()
|
||||
view.scene.cancel_crop_mode.assert_called()
|
||||
view.cancel_active_modes.assert_called()
|
||||
|
||||
|
||||
@patch('beeref.scene.BeeGraphicsScene.clearSelection')
|
||||
|
|
@ -547,26 +548,26 @@ def test_on_action_paste_internal(mimedata_mock, clear_mock, view):
|
|||
def test_on_action_paste_when_text(img_mock, text_mock, clear_mock, view):
|
||||
img_mock.return_value = QtGui.QImage()
|
||||
text_mock.return_value = 'foo bar'
|
||||
view.scene.cancel_crop_mode = MagicMock()
|
||||
view.cancel_active_modes = MagicMock()
|
||||
view.on_action_paste()
|
||||
assert len(view.scene.items()) == 1
|
||||
assert view.scene.items()[0].isSelected() is True
|
||||
assert view.scene.items()[0].toPlainText() == 'foo bar'
|
||||
clear_mock.assert_called_once_with()
|
||||
view.scene.cancel_crop_mode.assert_called_once_with()
|
||||
view.cancel_active_modes.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('beeref.scene.BeeGraphicsScene.clearSelection')
|
||||
@patch('PyQt6.QtGui.QClipboard.text')
|
||||
@patch('PyQt6.QtGui.QClipboard.image')
|
||||
def test_on_action_paste_when_empty(img_mock, text_mock, clear_mock, view):
|
||||
view.scene.cancel_crop_mode = MagicMock()
|
||||
view.cancel_active_modes = MagicMock()
|
||||
img_mock.return_value = QtGui.QImage()
|
||||
text_mock.return_value = ''
|
||||
view.on_action_paste()
|
||||
assert len(view.scene.items()) == 0
|
||||
clear_mock.assert_not_called()
|
||||
view.scene.cancel_crop_mode.assert_not_called()
|
||||
view.cancel_active_modes.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('beeref.view.BeeGraphicsView.on_action_copy')
|
||||
|
|
@ -644,6 +645,15 @@ def test_on_action_reset_transforms(view, item):
|
|||
assert item.scale() == 1
|
||||
|
||||
|
||||
def test_on_action_sample_color(view):
|
||||
view.cancel_active_modes = MagicMock()
|
||||
view.on_action_sample_color()
|
||||
assert view.active_mode == view.SAMPLE_COLOR_MODE
|
||||
assert isinstance(view.sample_color_widget, widgets.SampleColorWidget)
|
||||
assert view.viewport().cursor() == Qt.CursorShape.CrossCursor
|
||||
view.cancel_active_modes.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QWidget.create')
|
||||
@patch('PyQt6.QtWidgets.QWidget.destroy')
|
||||
@patch('PyQt6.QtWidgets.QWidget.show')
|
||||
|
|
@ -742,13 +752,13 @@ def test_on_action_deselect_all(view, item):
|
|||
|
||||
|
||||
def test_on_action_delete_items(view, item):
|
||||
view.scene.cancel_crop_mode = MagicMock()
|
||||
view.cancel_active_modes = MagicMock()
|
||||
view.scene.addItem(item)
|
||||
item.setSelected(True)
|
||||
view.on_action_delete_items()
|
||||
assert view.scene.items() == []
|
||||
assert view.undo_stack.isClean() is False
|
||||
view.scene.cancel_crop_mode.assert_called_once()
|
||||
view.cancel_active_modes.assert_called_once()
|
||||
|
||||
|
||||
@patch('beeref.widgets.ChangeOpacityDialog.__init__',
|
||||
|
|
@ -789,6 +799,18 @@ def test_on_action_grayscale(view):
|
|||
assert pixmapitem2.grayscale is False
|
||||
|
||||
|
||||
def test_cancel_active_modes_when_sample_color_mode(view):
|
||||
view.active_mode = view.SAMPLE_COLOR_MODE
|
||||
view.sample_color_widget = widgets.SampleColorWidget(
|
||||
view, MagicMock(), MagicMock())
|
||||
view.viewport().setCursor(Qt.CursorShape.CrossCursor)
|
||||
view.cancel_active_modes()
|
||||
|
||||
assert view.active_mode is None
|
||||
assert hasattr(view, 'sample_color_widget') is False
|
||||
assert view.viewport().cursor() == Qt.CursorShape.ArrowCursor
|
||||
|
||||
|
||||
@patch('PyQt6.QtGui.QUndoStack.isClean', return_value=True)
|
||||
def test_update_window_title_no_changes_no_filename(clear_mock, view):
|
||||
view.filename = None
|
||||
|
|
@ -920,9 +942,7 @@ def test_mouse_press_zoom(mouse_event_mock, view):
|
|||
event.button.return_value = Qt.MouseButton.MiddleButton
|
||||
event.modifiers.return_value = Qt.KeyboardModifier.ControlModifier
|
||||
view.mousePressEvent(event)
|
||||
assert view.zoom_active is True
|
||||
assert view.pan_active is False
|
||||
assert view.movewin_active is False
|
||||
assert view.active_mode == view.ZOOM_MODE
|
||||
assert view.event_start == QtCore.QPointF(10.0, 20.0)
|
||||
assert view.event_anchor == QtCore.QPointF(10.0, 20.0)
|
||||
mouse_event_mock.assert_not_called()
|
||||
|
|
@ -936,9 +956,7 @@ def test_mouse_press_pan_middle_drag(mouse_event_mock, view):
|
|||
event.button.return_value = Qt.MouseButton.MiddleButton
|
||||
event.modifiers.return_value = None
|
||||
view.mousePressEvent(event)
|
||||
assert view.pan_active is True
|
||||
assert view.zoom_active is False
|
||||
assert view.movewin_active is False
|
||||
assert view.active_mode == view.PAN_MODE
|
||||
assert view.event_start == QtCore.QPointF(10.0, 20.0)
|
||||
mouse_event_mock.assert_not_called()
|
||||
view.cursor() == Qt.CursorShape.ClosedHandCursor
|
||||
|
|
@ -952,15 +970,71 @@ def test_mouse_press_pan_alt_left_drag(mouse_event_mock, view):
|
|||
event.button.return_value = Qt.MouseButton.LeftButton
|
||||
event.modifiers.return_value = Qt.KeyboardModifier.AltModifier
|
||||
view.mousePressEvent(event)
|
||||
assert view.pan_active is True
|
||||
assert view.zoom_active is False
|
||||
assert view.movewin_active is False
|
||||
assert view.active_mode == view.PAN_MODE
|
||||
assert view.event_start == QtCore.QPointF(10.0, 20.0)
|
||||
mouse_event_mock.assert_not_called()
|
||||
view.cursor() == Qt.CursorShape.ClosedHandCursor
|
||||
event.accept.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('beeref.widgets.BeeNotification')
|
||||
@patch('PyQt6.QtWidgets.QGraphicsView.mousePressEvent')
|
||||
def test_mouse_press_sample_color_when_color(
|
||||
mouse_event_mock, notification_mock, view):
|
||||
view.scene.sample_color_at = MagicMock(
|
||||
return_value=QtGui.QColor(255, 0, 0, 255))
|
||||
view.active_mode = view.SAMPLE_COLOR_MODE
|
||||
event = MagicMock()
|
||||
event.pos.return_value = QtCore.QPoint(2, 2)
|
||||
event.button.return_value = Qt.MouseButton.LeftButton
|
||||
|
||||
view.mousePressEvent(event)
|
||||
assert QtWidgets.QApplication.clipboard().text() == '#ff0000'
|
||||
notification_mock.assert_called_once_with(
|
||||
view, 'Copied color to clipboard: #ff0000')
|
||||
assert view.active_mode is None
|
||||
view.scene.sample_color_at.assert_called_once()
|
||||
mouse_event_mock.assert_not_called()
|
||||
|
||||
|
||||
@patch('beeref.widgets.BeeNotification')
|
||||
@patch('PyQt6.QtWidgets.QGraphicsView.mousePressEvent')
|
||||
def test_mouse_press_sample_color_when_color_with_alpha(
|
||||
mouse_event_mock, notification_mock, view):
|
||||
view.scene.sample_color_at = MagicMock(
|
||||
return_value=QtGui.QColor(255, 0, 0, 100))
|
||||
view.active_mode = view.SAMPLE_COLOR_MODE
|
||||
event = MagicMock()
|
||||
event.pos.return_value = QtCore.QPoint(2, 2)
|
||||
event.button.return_value = Qt.MouseButton.LeftButton
|
||||
|
||||
view.mousePressEvent(event)
|
||||
assert QtWidgets.QApplication.clipboard().text() == '#ff000064'
|
||||
notification_mock.assert_called_once_with(
|
||||
view, 'Copied color to clipboard: #ff000064')
|
||||
assert view.active_mode is None
|
||||
view.scene.sample_color_at.assert_called_once()
|
||||
mouse_event_mock.assert_not_called()
|
||||
|
||||
|
||||
@patch('beeref.widgets.BeeNotification')
|
||||
@patch('PyQt6.QtWidgets.QGraphicsView.mousePressEvent')
|
||||
def test_mouse_press_sample_color_when_no_color(
|
||||
mouse_event_mock, notification_mock, view):
|
||||
view.scene.sample_color_at = MagicMock(return_value=None)
|
||||
view.active_mode = view.SAMPLE_COLOR_MODE
|
||||
event = MagicMock()
|
||||
event.pos.return_value = QtCore.QPoint(2, 2)
|
||||
event.button.return_value = Qt.MouseButton.LeftButton
|
||||
|
||||
view.mousePressEvent(event)
|
||||
notification_mock.assert_not_called()
|
||||
assert view.active_mode is None
|
||||
view.scene.sample_color_at.assert_called_once()
|
||||
mouse_event_mock.assert_not_called()
|
||||
event.accept.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QGraphicsView.mousePressEvent')
|
||||
@patch('beeref.view.BeeGraphicsView.cursor')
|
||||
def test_mouse_press_move_window(cursor_mock, mouse_event_mock, view):
|
||||
|
|
@ -971,8 +1045,7 @@ def test_mouse_press_move_window(cursor_mock, mouse_event_mock, view):
|
|||
event.modifiers.return_value = (
|
||||
Qt.KeyboardModifier.AltModifier | Qt.KeyboardModifier.ControlModifier)
|
||||
view.mousePressEvent(event)
|
||||
assert view.pan_active is False
|
||||
assert view.zoom_active is False
|
||||
assert view.active_mode is None
|
||||
assert view.movewin_active is True
|
||||
assert view.event_start == view.mapToGlobal(QtCore.QPointF(10.0, 20.0))
|
||||
mouse_event_mock.assert_not_called()
|
||||
|
|
@ -987,6 +1060,16 @@ def test_mouse_press_when_move_window_active(mouse_event_mock, view):
|
|||
mouse_event_mock.assert_not_called()
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QGraphicsView.keyPressEvent')
|
||||
def test_key_press_when_sample_color_mode(key_event_mock, view):
|
||||
view.active_mode = view.SAMPLE_COLOR_MODE
|
||||
event = MagicMock()
|
||||
view.keyPressEvent(event)
|
||||
assert view.active_mode is None
|
||||
event.accept.assert_called_once_with()
|
||||
key_event_mock.assert_not_called()
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QGraphicsView.keyPressEvent')
|
||||
def test_key_press_when_move_window_active(key_event_mock, view):
|
||||
view.movewin_active = True
|
||||
|
|
@ -1001,9 +1084,7 @@ def test_mouse_press_unhandled(mouse_event_mock, view):
|
|||
event.button.return_value = Qt.MouseButton.LeftButton
|
||||
event.modifiers.return_value = None
|
||||
view.mousePressEvent(event)
|
||||
assert view.pan_active is False
|
||||
assert view.zoom_active is False
|
||||
assert view.movewin_active is False
|
||||
assert view.active_mode is None
|
||||
mouse_event_mock.assert_called_once_with(event)
|
||||
event.accept.assert_not_called()
|
||||
|
||||
|
|
@ -1011,7 +1092,7 @@ def test_mouse_press_unhandled(mouse_event_mock, view):
|
|||
@patch('PyQt6.QtWidgets.QGraphicsView.mouseMoveEvent')
|
||||
@patch('beeref.view.BeeGraphicsView.pan')
|
||||
def test_mouse_move_pan(pan_mock, mouse_event_mock, view):
|
||||
view.pan_active = True
|
||||
view.active_mode = view.PAN_MODE
|
||||
view.event_start = QtCore.QPointF(55.0, 66.0)
|
||||
event = MagicMock()
|
||||
event.position.return_value = QtCore.QPointF(10.0, 20.0)
|
||||
|
|
@ -1024,7 +1105,7 @@ def test_mouse_move_pan(pan_mock, mouse_event_mock, view):
|
|||
@patch('PyQt6.QtWidgets.QGraphicsView.mouseMoveEvent')
|
||||
@patch('beeref.view.BeeGraphicsView.zoom')
|
||||
def test_mouse_move_zoom(zoom_mock, mouse_event_mock, view):
|
||||
view.zoom_active = True
|
||||
view.active_mode = view.ZOOM_MODE
|
||||
view.event_anchor = QtCore.QPointF(55.0, 66.0)
|
||||
view.event_start = QtCore.QPointF(10.0, 20.0)
|
||||
event = MagicMock()
|
||||
|
|
@ -1035,6 +1116,24 @@ def test_mouse_move_zoom(zoom_mock, mouse_event_mock, view):
|
|||
event.accept.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QGraphicsView.mouseMoveEvent')
|
||||
def test_mouse_move_sample_color(mouse_event_mock, view):
|
||||
view.active_mode = view.SAMPLE_COLOR_MODE
|
||||
view.scene.sample_color_at = MagicMock(
|
||||
return_value=QtGui.QColor(255, 0, 0, 255))
|
||||
view.sample_color_widget = MagicMock()
|
||||
event = MagicMock()
|
||||
event.pos.return_value = QtCore.QPoint(2, 2)
|
||||
event.position.return_value = QtCore.QPointF(10.0, 18.0)
|
||||
view.mouseMoveEvent(event)
|
||||
view.scene.sample_color_at.assert_called_once()
|
||||
view.sample_color_widget.update.assert_called_once_with(
|
||||
QtCore.QPointF(10.0, 18.0),
|
||||
QtGui.QColor(255, 0, 0, 255))
|
||||
mouse_event_mock.assert_not_called()
|
||||
event.accept.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('PyQt6.QtWidgets.QGraphicsView.mouseMoveEvent')
|
||||
@patch('PyQt6.QtWidgets.QWidget.move')
|
||||
def test_mouse_move_movewin(move_mock, mouse_event_mock, view):
|
||||
|
|
@ -1060,11 +1159,11 @@ def test_mouse_move_unhandled(mouse_event_mock, view):
|
|||
@patch('PyQt6.QtWidgets.QGraphicsView.mouseReleaseEvent')
|
||||
def test_mouse_release_pan(mouse_event_mock, view):
|
||||
event = MagicMock()
|
||||
view.pan_active = True
|
||||
view.active_mode = view.PAN_MODE
|
||||
view.setCursor(Qt.CursorShape.ClosedHandCursor)
|
||||
view.mouseReleaseEvent(event)
|
||||
mouse_event_mock.assert_not_called()
|
||||
assert view.pan_active is False
|
||||
assert view.active_mode is None
|
||||
event.accept.assert_called_once_with()
|
||||
view.cursor() == Qt.CursorShape.ArrowCursor
|
||||
|
||||
|
|
@ -1072,10 +1171,10 @@ def test_mouse_release_pan(mouse_event_mock, view):
|
|||
@patch('PyQt6.QtWidgets.QGraphicsView.mouseReleaseEvent')
|
||||
def test_mouse_release_zoom(mouse_event_mock, view):
|
||||
event = MagicMock()
|
||||
view.zoom_active = True
|
||||
view.active_mode = view.ZOOM_MODE
|
||||
view.mouseReleaseEvent(event)
|
||||
mouse_event_mock.assert_not_called()
|
||||
assert view.zoom_active is False
|
||||
assert view.active_mode is None
|
||||
event.accept.assert_called_once_with()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
from unittest.mock import patch, MagicMock
|
||||
|
||||
from PyQt6 import QtCore, QtWidgets, QtGui
|
||||
from PyQt6.QtCore import Qt
|
||||
|
||||
from beeref.config import logfile_name
|
||||
from beeref.widgets import (
|
||||
BeeNotification,
|
||||
ChangeOpacityDialog,
|
||||
DebugLogDialog,
|
||||
SampleColorWidget,
|
||||
SceneToPixmapExporterDialog,
|
||||
)
|
||||
|
||||
|
|
@ -94,3 +98,44 @@ def test_change_opacity_dialog_reject(view, item):
|
|||
dlg.reject()
|
||||
assert item.opacity() == 0.6
|
||||
assert len(stack) == 0
|
||||
|
||||
|
||||
@patch('PyQt6.QtCore.QTimer.singleShot')
|
||||
def test_bee_notification(single_shot_mock, view):
|
||||
widget = BeeNotification(view, 'Hello World')
|
||||
assert widget.label.text() == 'Hello World'
|
||||
single_shot_mock.assert_called_once_with(1000 * 3, widget.deleteLater)
|
||||
|
||||
|
||||
def test_sample_color_widget(view):
|
||||
widget = SampleColorWidget(
|
||||
view, QtCore.QPoint(2, 5), QtGui.QColor(255, 0, 0))
|
||||
assert widget.color == QtGui.QColor(255, 0, 0)
|
||||
assert widget.geometry() == QtCore.QRect(12, 15, 50, 50)
|
||||
|
||||
widget.update(QtCore.QPoint(13, 15), QtGui.QColor(0, 255, 0))
|
||||
assert widget.color == QtGui.QColor(0, 255, 0)
|
||||
assert widget.geometry() == QtCore.QRect(23, 25, 50, 50)
|
||||
|
||||
|
||||
def test_sample_color_widget_paint_event_when_color(view):
|
||||
widget = SampleColorWidget(
|
||||
view, QtCore.QPoint(2, 5), QtGui.QColor(255, 0, 0))
|
||||
with patch('PyQt6.QtGui.QPainter') as painter_cls_mock:
|
||||
painter_mock = MagicMock()
|
||||
painter_cls_mock.return_value = painter_mock
|
||||
widget.paintEvent(MagicMock())
|
||||
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
|
||||
painter_mock.setBrush.assert_called_once_with(brush)
|
||||
painter_mock.drawRect.assert_called_once_with(0, 0, 50, 50)
|
||||
|
||||
|
||||
def test_sample_color_widget_paint_event_when_no_color(view):
|
||||
widget = SampleColorWidget(view, QtCore.QPoint(2, 5), None)
|
||||
with patch('PyQt6.QtGui.QPainter') as painter_cls_mock:
|
||||
painter_mock = MagicMock()
|
||||
painter_cls_mock.return_value = painter_mock
|
||||
widget.paintEvent(MagicMock())
|
||||
brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 0))
|
||||
painter_mock.setBrush.assert_called_once_with(brush)
|
||||
painter_mock.drawRect.assert_called_once_with(0, 0, 50, 50)
|
||||
|
|
|
|||
Loading…
Reference in a new issue