Refactor comments to English for consistency

- Updated comments in `__main__.py`, `items.py`, `assets/__init__.py`, `image.py`, `draw_floating_menu.py`, and `floating_menu.py` to English for better readability and maintainability.
- Ensured that all comments accurately describe the corresponding code functionality.
This commit is contained in:
Dmitry 2025-11-21 13:42:41 +03:00
parent dc733cfc23
commit c8b600a3c4
6 changed files with 129 additions and 129 deletions

View file

@ -64,15 +64,15 @@ class BeeRefMainWindow(QtWidgets.QMainWindow):
self.show()
def closeEvent(self, event):
# Проверяем наличие несохраненных изменений
# Check for unsaved changes
confirm = self.view.get_confirmation_unsaved_changes(
'There are unsaved changes. Are you sure you want to quit?')
if not confirm:
# Пользователь отменил закрытие
# User cancelled closing
event.ignore()
return
# Сохраняем геометрию окна
# Save window geometry
geom = self.saveGeometry()
self.view.settings.setValue('MainWindow/geometry', geom)
event.accept()

View file

@ -59,20 +59,20 @@ class BeeAssets:
pixmap, int(hotspot[0]/scaling), int(hotspot[1]/scaling))
def cursor_from_svg(self, filename, hotspot, size=24):
"""Создает курсор из SVG файла."""
"""Creates cursor from SVG file."""
app = QtWidgets.QApplication.instance()
scaling = app.primaryScreen().devicePixelRatio()
# Загружаем SVG
# Load SVG
svg_path = str(self.PATH.joinpath(filename))
renderer = QtSvg.QSvgRenderer(svg_path)
# Создаем pixmap нужного размера
# Create pixmap of required size
pixmap_size = int(size * scaling)
pixmap = QtGui.QPixmap(pixmap_size, pixmap_size)
pixmap.fill(QtGui.QColor(0, 0, 0, 0)) # Прозрачный фон
pixmap.fill(QtGui.QColor(0, 0, 0, 0)) # Transparent background
# Рендерим SVG в pixmap
# Render SVG to pixmap
painter = QtGui.QPainter(pixmap)
renderer.render(painter)
painter.end()

View file

@ -30,7 +30,7 @@ logger = logging.getLogger(__name__)
def is_gif_file(path):
"""Проверяет, является ли файл GIF."""
"""Checks if file is a GIF."""
if isinstance(path, str):
ext = os.path.splitext(path)[1].lower()
return ext == '.gif'
@ -95,9 +95,9 @@ def exif_rotated_image(path=None):
def load_image(path):
if isinstance(path, str):
path = os.path.normpath(path)
# Проверяем, является ли файл GIF
# Check if file is a GIF
if is_gif_file(path):
return (None, path) # Возвращаем None для изображения, путь для GIF
return (None, path) # Return None for image, path for GIF
return (exif_rotated_image(path), path)
if path.isLocalFile():
path = os.path.normpath(path.toLocalFile())

View file

@ -745,7 +745,7 @@ class BeeDrawItem(BeeItemMixin, QtWidgets.QGraphicsPathItem):
"""Class for freehand drawing items."""
TYPE = 'draw'
CLICKABLE_PADDING = 8.0 # Отступ вокруг линии для увеличения кликабельной области
CLICKABLE_PADDING = 8.0 # Padding around line to increase clickable area
def __init__(self, path=None, **kwargs):
super().__init__()
@ -753,9 +753,9 @@ class BeeDrawItem(BeeItemMixin, QtWidgets.QGraphicsPathItem):
logger.debug(f'Initialized {self}')
self.is_image = False
self.init_selectable()
self.is_editable = False # Рисование не редактируется через двойной клик
self.is_editable = False # Drawing is not editable via double-click
# Настройки пера по умолчанию
# Default pen settings
self.pen_color = QtGui.QColor(*COLORS['Scene:Text'])
self.pen_width = 8
self.pen_style = 'solid' # 'solid', 'dashed', 'arrow', '<-', '<->'
@ -766,7 +766,7 @@ class BeeDrawItem(BeeItemMixin, QtWidgets.QGraphicsPathItem):
self.setPath(path)
def setPath(self, path):
"""Устанавливает путь и обновляет геометрию."""
"""Sets path and updates geometry."""
self.prepareGeometryChange()
super().setPath(path)
self.update()
@ -776,7 +776,7 @@ class BeeDrawItem(BeeItemMixin, QtWidgets.QGraphicsPathItem):
data = kwargs.get('data', {})
item = cls()
# Восстановление пути из данных
# Restore path from data
if 'path' in data and data['path']:
path = QtGui.QPainterPath()
path_data = data['path']
@ -800,7 +800,7 @@ class BeeDrawItem(BeeItemMixin, QtWidgets.QGraphicsPathItem):
return f'Drawing ({self.path().elementCount()} points)'
def get_extra_save_data(self):
"""Сохраняет данные рисунка для сериализации."""
"""Saves drawing data for serialization."""
path = self.path()
path_data = []
for i in range(path.elementCount()):
@ -815,7 +815,7 @@ class BeeDrawItem(BeeItemMixin, QtWidgets.QGraphicsPathItem):
}
def _update_pen(self):
"""Обновляет перо с текущими настройками."""
"""Updates pen with current settings."""
if self.pen_style == 'dashed':
pen_style = QtCore.Qt.PenStyle.DashLine
else:
@ -828,19 +828,19 @@ class BeeDrawItem(BeeItemMixin, QtWidgets.QGraphicsPathItem):
self.setPen(pen)
def set_pen_color(self, color: QtGui.QColor):
"""Устанавливает цвет пера."""
"""Sets pen color."""
self.pen_color = color
self._update_pen()
self.update()
def set_pen_width(self, width: int):
"""Устанавливает толщину пера."""
self.pen_width = max(1, min(width, 50)) # Ограничение 1-50
"""Sets pen width."""
self.pen_width = max(1, min(width, 50)) # Limit 1-50
self._update_pen()
self.update()
def set_pen_style(self, style: str):
"""Устанавливает стиль линии: 'solid', 'dashed', 'arrow', '<-', '<->'."""
"""Sets line style: 'solid', 'dashed', 'arrow', '<-', '<->'."""
if style in ('solid', 'dashed', 'arrow', '<-', '<->'):
self.pen_style = style
self._update_pen()
@ -861,31 +861,31 @@ class BeeDrawItem(BeeItemMixin, QtWidgets.QGraphicsPathItem):
return item
def bounding_rect_unselected(self):
"""Возвращает границы элемента без учета выделения."""
"""Returns item bounds without selection."""
path = self.path()
if path.isEmpty():
return QtCore.QRectF()
# Получаем boundingRect напрямую из пути
# Get boundingRect directly from path
base_rect = path.boundingRect()
# Добавляем отступ для толщины пера и кликабельной области
# Add margin for pen width and clickable area
margin = (self.pen_width / 2.0) + self.CLICKABLE_PADDING
return base_rect.marginsAdded(
QtCore.QMarginsF(margin, margin, margin, margin))
def shape(self):
"""Возвращает прямоугольную кликабельную область, как в PureRef."""
"""Returns rectangular clickable area, like in PureRef."""
path = QtGui.QPainterPath()
rect = self.bounding_rect_unselected()
# Если элемент выделен и есть ручки, добавляем области для ручек
# If item is selected and has handles, add handle areas
if self.has_selection_handles():
margin = self.select_resize_size / 2
rect = rect.marginsAdded(
QtCore.QMarginsF(margin, margin, margin, margin))
path.addRect(rect)
# Добавляем области для ручек поворота в углах
# Add rotation handle areas at corners
for corner in self.corners:
path.addPath(self.get_rotate_bounds(corner))
else:
@ -894,74 +894,74 @@ class BeeDrawItem(BeeItemMixin, QtWidgets.QGraphicsPathItem):
return path
def contains(self, point):
"""Проверяет, попадает ли точка в прямоугольную область линии."""
# Используем boundingRect для прямоугольной области клика
"""Checks if point falls within rectangular line area."""
# Use boundingRect for rectangular click area
return self.bounding_rect_unselected().contains(point)
def _get_path_end_points(self, path):
"""Получает последние две точки пути для определения направления стрелки."""
"""Gets last two path points to determine arrow direction."""
if path.elementCount() < 2:
return None, None
# Получаем последнюю точку пути
# Get last path point
last_point = path.pointAtPercent(1.0)
# Получаем предпоследнюю точку (близко к концу)
# Get second-to-last point (close to end)
if path.elementCount() >= 2:
prev_point = path.pointAtPercent(0.95) # 95% от пути
prev_point = path.pointAtPercent(0.95) # 95% of path
else:
prev_point = path.pointAtPercent(0.0)
return prev_point, last_point
def _get_path_start_points(self, path):
"""Получает первые две точки пути для определения направления стрелки."""
"""Gets first two path points to determine arrow direction."""
if path.elementCount() < 2:
return None, None
# Получаем первую точку пути
# Get first path point
first_point = path.pointAtPercent(0.0)
# Получаем вторую точку (близко к началу)
# Get second point (close to start)
if path.elementCount() >= 2:
second_point = path.pointAtPercent(0.05) # 5% от пути
second_point = path.pointAtPercent(0.05) # 5% of path
else:
second_point = path.pointAtPercent(1.0)
return first_point, second_point
def _draw_arrow_right(self, painter, path):
"""Рисует стрелку вправо в конце линии."""
"""Draws arrow to the right at the end of line."""
if path.elementCount() < 2:
return
# Получаем последние две точки для определения направления
# Get last two points to determine direction
prev_point, last_point = self._get_path_end_points(path)
if prev_point is None or last_point is None:
return
# Вычисляем направление стрелки
# Calculate arrow direction
dx = last_point.x() - prev_point.x()
dy = last_point.y() - prev_point.y()
length = (dx * dx + dy * dy) ** 0.5
if length == 0:
return
# Нормализуем вектор направления
# Normalize direction vector
dx /= length
dy /= length
# Размер стрелки зависит от толщины линии
# Arrow size depends on line width
arrow_size = max(self.pen_width * 3, 8)
# Угол стрелки
angle = 0.5 # примерно 30 градусов
# Arrow angle
angle = 0.5 # approximately 30 degrees
# Координаты конца стрелки
# Arrow end coordinates
end_x = last_point.x()
end_y = last_point.y()
# Координаты боковых точек стрелки
# Arrow side point coordinates
perp_x = -dy
perp_y = dx
arrow_x1 = end_x - arrow_size * dx + arrow_size * angle * perp_x
@ -969,7 +969,7 @@ class BeeDrawItem(BeeItemMixin, QtWidgets.QGraphicsPathItem):
arrow_x2 = end_x - arrow_size * dx - arrow_size * angle * perp_x
arrow_y2 = end_y - arrow_size * dy - arrow_size * angle * perp_y
# Рисуем стрелку
# Draw arrow
arrow_path = QtGui.QPainterPath()
arrow_path.moveTo(end_x, end_y)
arrow_path.lineTo(arrow_x1, arrow_y1)
@ -983,36 +983,36 @@ class BeeDrawItem(BeeItemMixin, QtWidgets.QGraphicsPathItem):
painter.drawPath(arrow_path)
def _draw_arrow_left(self, painter, path):
"""Рисует стрелку влево в начале линии."""
"""Draws arrow to the left at the start of line."""
if path.elementCount() < 2:
return
# Получаем первые две точки для определения направления
# Get first two points to determine direction
first_point, second_point = self._get_path_start_points(path)
if first_point is None or second_point is None:
return
# Вычисляем направление стрелки (от второй точки к первой)
# Calculate arrow direction (from second point to first)
dx = first_point.x() - second_point.x()
dy = first_point.y() - second_point.y()
length = (dx * dx + dy * dy) ** 0.5
if length == 0:
return
# Нормализуем вектор направления
# Normalize direction vector
dx /= length
dy /= length
# Размер стрелки зависит от толщины линии
# Arrow size depends on line width
arrow_size = max(self.pen_width * 3, 8)
# Угол стрелки
angle = 0.5 # примерно 30 градусов
# Arrow angle
angle = 0.5 # approximately 30 degrees
# Координаты начала стрелки
# Arrow start coordinates
start_x = first_point.x()
start_y = first_point.y()
# Координаты боковых точек стрелки
# Arrow side point coordinates
perp_x = -dy
perp_y = dx
arrow_x1 = start_x - arrow_size * dx + arrow_size * angle * perp_x
@ -1020,7 +1020,7 @@ class BeeDrawItem(BeeItemMixin, QtWidgets.QGraphicsPathItem):
arrow_x2 = start_x - arrow_size * dx - arrow_size * angle * perp_x
arrow_y2 = start_y - arrow_size * dy - arrow_size * angle * perp_y
# Рисуем стрелку
# Draw arrow
arrow_path = QtGui.QPainterPath()
arrow_path.moveTo(start_x, start_y)
arrow_path.lineTo(arrow_x1, arrow_y1)
@ -1034,11 +1034,11 @@ class BeeDrawItem(BeeItemMixin, QtWidgets.QGraphicsPathItem):
painter.drawPath(arrow_path)
def _draw_arrow_both(self, painter, path):
"""Рисует стрелки в обе стороны линии."""
"""Draws arrows on both sides of line."""
if path.elementCount() < 2:
return
# Рисуем стрелку вправо (в конце)
# Draw arrow to the right (at end)
prev_point, last_point = self._get_path_end_points(path)
if prev_point is not None and last_point is not None:
dx = last_point.x() - prev_point.x()
@ -1070,7 +1070,7 @@ class BeeDrawItem(BeeItemMixin, QtWidgets.QGraphicsPathItem):
QtCore.Qt.PenJoinStyle.RoundJoin))
painter.drawPath(arrow_path)
# Рисуем стрелку влево (в начале)
# Draw arrow to the left (at start)
first_point, second_point = self._get_path_start_points(path)
if first_point is not None and second_point is not None:
dx = first_point.x() - second_point.x()
@ -1103,15 +1103,15 @@ class BeeDrawItem(BeeItemMixin, QtWidgets.QGraphicsPathItem):
painter.drawPath(arrow_path)
def paint(self, painter, option, widget):
"""Отрисовка пути с рамкой выделения."""
# Отключаем стандартную отрисовку Qt для выделенных элементов
"""Renders path with selection outline."""
# Disable standard Qt rendering for selected items
option.state &= ~QtWidgets.QStyle.StateFlag.State_Selected
option.state &= ~QtWidgets.QStyle.StateFlag.State_HasFocus
# Рисуем основную линию
# Draw main line
super().paint(painter, option, widget)
path = self.path()
# Рисуем стрелку в зависимости от стиля
# Draw arrow depending on style
if self.pen_style == 'arrow':
self._draw_arrow_right(painter, path)
elif self.pen_style == '<-':
@ -1122,13 +1122,13 @@ class BeeDrawItem(BeeItemMixin, QtWidgets.QGraphicsPathItem):
self.paint_selectable(painter, option, widget)
def paint_debug(self, painter, option, widget):
"""Переопределяем для полного отключения отладочной информации."""
# Полностью отключаем отладочную информацию для линий
"""Override to completely disable debug information."""
# Completely disable debug information for lines
pass
def paint_selectable(self, painter, option, widget):
"""Переопределяем для убирания пунктирной обводки (отладочной информации)."""
# Не вызываем paint_debug, чтобы убрать пунктирную обводку
"""Override to remove dashed outline (debug information)."""
# Don't call paint_debug to remove dashed outline
# self.paint_debug(painter, option, widget)
if not self.has_selection_outline():
@ -1152,7 +1152,7 @@ class BeeDrawItem(BeeItemMixin, QtWidgets.QGraphicsPathItem):
painter.drawPoint(corner)
def copy_to_clipboard(self, clipboard):
"""Для рисования копирование не поддерживается."""
"""Copying is not supported for drawings."""
pass
@ -1231,6 +1231,6 @@ class BeeErrorItem(BeeItemMixin, QtWidgets.QGraphicsTextItem):
clipboard.setText(self.toPlainText())
# Импортируем GIF item для регистрации в item_registry
# Import GIF item for registration in item_registry
from beeref.gif_item import BeeGifItem # noqa: E402, F401

View file

@ -25,15 +25,15 @@ class UpwardComboBox(QtWidgets.QComboBox):
def showPopup(self):
"""Override to show popup above the combobox."""
super().showPopup()
# Используем QTimer для получения popup после его создания
# Use QTimer to get popup after it's created
QtCore.QTimer.singleShot(0, self._reposition_popup)
def _reposition_popup(self):
"""Перемещает popup выше combobox."""
# Ищем активный popup виджет
"""Moves popup above the combobox."""
# Find active popup widget
popup = QtWidgets.QApplication.activePopupWidget()
if not popup:
# Альтернативный способ - найти через view
# Alternative method - find through view
view = self.view()
if view:
popup = view.parent()
@ -41,9 +41,9 @@ class UpwardComboBox(QtWidgets.QComboBox):
popup = popup.parent()
if popup:
# Получаем глобальную позицию combobox
# Get global position of combobox
global_pos = self.mapToGlobal(QtCore.QPoint(0, 0))
# Вычисляем новую позицию выше combobox
# Calculate new position above combobox
popup_height = popup.height()
new_y = global_pos.y() - popup_height
popup.move(global_pos.x(), new_y)
@ -56,7 +56,7 @@ class DrawFloatingMenu(FloatingMenu):
super().__init__(parent, view)
self.current_item: Optional["BeeDrawItem"] = None
# Кнопка выбора цвета через colorpicker
# Color selection button via colorpicker
assets = BeeAssets()
icons_path = assets.PATH.joinpath('icons')
palette_icon = QtGui.QIcon(str(icons_path.joinpath('palette.svg')))
@ -68,15 +68,15 @@ class DrawFloatingMenu(FloatingMenu):
)
self.color_btn.setToolTip("Color")
# Combobox для выбора стиля линии
# Combobox for line style selection
self.add_separator()
self.style_combo = self.add_style_combobox()
# Селектор толщины пера
# Pen width selector
self.add_separator()
self.width_slider = self.add_width_slider()
# Кнопка закрытия меню (отмена режима рисования)
# Close menu button (cancel drawing mode)
self.add_separator()
close_icon = QtGui.QIcon(str(icons_path.joinpath('close.svg')))
self.close_button = self.add_button(
@ -87,11 +87,11 @@ class DrawFloatingMenu(FloatingMenu):
self.close_button.setToolTip("Close")
def add_style_combobox(self):
"""Добавляет combobox для выбора стиля линии."""
"""Adds combobox for line style selection."""
assets = BeeAssets()
icons_path = assets.PATH.joinpath('icons')
# Загружаем иконки для стилей
# Load icons for styles
solid_icon = QtGui.QIcon(str(icons_path.joinpath('line-solid.svg')))
dashed_icon = QtGui.QIcon(str(icons_path.joinpath('line-dashed.svg')))
arrow_icon = QtGui.QIcon(str(icons_path.joinpath('line-arrow.svg')))
@ -103,20 +103,20 @@ class DrawFloatingMenu(FloatingMenu):
combo.setMinimumWidth(40)
combo.setIconSize(QtCore.QSize(32, 32))
# Добавляем элементы с иконками
# Add items with icons
combo.addItem(solid_icon, "", 'solid')
combo.addItem(dashed_icon, "", 'dashed')
combo.addItem(arrow_icon, "", 'arrow')
combo.addItem(arrow_left_icon, "", '<-')
combo.addItem(arrow_both_icon, "", '<->')
# Устанавливаем иконки для элементов
# Set icons for items
combo.setItemIcon(0, solid_icon)
combo.setItemIcon(1, dashed_icon)
combo.setItemIcon(2, arrow_icon)
combo.setItemIcon(3, arrow_left_icon)
combo.setItemIcon(4, arrow_both_icon)
combo.setCurrentIndex(0) # По умолчанию solid
combo.setCurrentIndex(0) # Default to solid
combo.currentIndexChanged.connect(self._on_style_changed)
combo.setToolTip("Line style")
@ -124,7 +124,7 @@ class DrawFloatingMenu(FloatingMenu):
return combo
def _on_color_clicked(self) -> None:
"""Открывает диалог выбора цвета."""
"""Opens color selection dialog."""
if not self.current_item:
return
@ -136,7 +136,7 @@ class DrawFloatingMenu(FloatingMenu):
self.set_pen_color(color)
def _on_style_changed(self, index: int) -> None:
"""Обработчик изменения стиля линии."""
"""Handler for line style change."""
if not self.current_item:
return
style = self.style_combo.itemData(index)
@ -144,7 +144,7 @@ class DrawFloatingMenu(FloatingMenu):
self.set_pen_style(style)
def add_width_slider(self):
"""Добавляет слайдер для выбора толщины пера."""
"""Adds slider for pen width selection."""
container = QtWidgets.QWidget()
layout = QtWidgets.QHBoxLayout(container)
layout.setContentsMargins(0, 0, 0, 0)
@ -162,11 +162,11 @@ class DrawFloatingMenu(FloatingMenu):
return slider
def show_for_item(self, item: "BeeDrawItem") -> None:
"""Показывает меню для выбранного элемента рисования."""
"""Shows menu for selected drawing item."""
self.current_item = item
if item:
self.width_slider.setValue(item.pen_width)
# Устанавливаем текущий стиль в combobox
# Set current style in combobox
style_index = self.style_combo.findData(item.pen_style)
if style_index >= 0:
self.style_combo.blockSignals(True)
@ -175,29 +175,29 @@ class DrawFloatingMenu(FloatingMenu):
super().show_for_item(item)
def set_pen_color(self, color: QtGui.QColor):
"""Устанавливает цвет пера для выбранного элемента."""
"""Sets pen color for selected item."""
if self.current_item:
self.current_item.set_pen_color(color)
def set_pen_width(self, width: int):
"""Устанавливает толщину пера для выбранного элемента."""
"""Sets pen width for selected item."""
if self.current_item:
self.current_item.set_pen_width(width)
def set_pen_style(self, style: str):
"""Устанавливает стиль линии для выбранного элемента."""
"""Sets line style for selected item."""
if self.current_item:
self.current_item.set_pen_style(style)
def _on_close_clicked(self) -> None:
"""Закрывает меню и отменяет режим рисования."""
"""Closes menu and cancels drawing mode."""
self.view.cancel_drawing_mode()
self.hide_menu()
def keyPressEvent(self, event: QtGui.QKeyEvent) -> None:
"""Обрабатывает нажатия клавиш."""
"""Handles key press events."""
if event.key() == QtCore.Qt.Key.Key_Escape:
# ESC закрывает меню и отменяет режим рисования
# ESC closes menu and cancels drawing mode
self.view.cancel_drawing_mode()
self.hide_menu()
event.accept()

View file

@ -39,10 +39,10 @@ class FloatingMenu(QtWidgets.QWidget):
self._cached_viewport_size: Optional[QtCore.QSize] = None
self._cached_window_pos: Optional[QtCore.QPoint] = None
# Таймер для отслеживания перемещения главного окна
# Timer for tracking main window movement
self._position_timer = QtCore.QTimer(self)
self._position_timer.timeout.connect(self._check_window_position)
self._position_timer.setInterval(50) # Проверка каждые 50мс
self._position_timer.setInterval(50) # Check every 50ms
# Main layout with uniform spacing
self._layout = QtWidgets.QHBoxLayout(self)
@ -56,7 +56,7 @@ class FloatingMenu(QtWidgets.QWidget):
self.hide()
def _check_window_position(self) -> None:
"""Проверяет позицию viewport и обновляет позицию меню при необходимости."""
"""Checks viewport position and updates menu position if needed."""
if not self.isVisible():
return
@ -68,11 +68,11 @@ class FloatingMenu(QtWidgets.QWidget):
if viewport is None:
return
# Получаем позицию viewport в глобальных координатах
# Get viewport position in global coordinates
view_rect = viewport.rect()
current_viewport_pos = viewport.mapToGlobal(view_rect.topLeft())
# Если позиция viewport изменилась, обновляем позицию меню
# If viewport position changed, update menu position
if self._cached_window_pos is not None and self._cached_window_pos != current_viewport_pos:
self.update_position()
@ -80,50 +80,50 @@ class FloatingMenu(QtWidgets.QWidget):
def _apply_rounded_mask(self) -> None:
"""
Применяет закругленную маску к виджету с антиалиасингом.
Реализация на основе подхода из статьи VK Teams.
Applies rounded mask to widget with antialiasing.
Implementation based on approach from VK Teams article.
"""
size = self.size()
if size.width() == 0 or size.height() == 0:
return
# Для более плавного сглаживания используем QBitmap с антиалиасингом
# Создаем изображение с увеличенным разрешением (как в статье)
# Use QBitmap with antialiasing for smoother rendering
# Create image with increased resolution (as in article)
scale_factor = 2
scaled_size = QtCore.QSize(
int(size.width() * scale_factor),
int(size.height() * scale_factor)
)
# Создаем QPixmap для рисования с антиалиасингом
# Create QPixmap for drawing with antialiasing
pixmap = QtGui.QPixmap(scaled_size)
pixmap.fill(QtCore.Qt.GlobalColor.transparent)
painter = QtGui.QPainter(pixmap)
painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)
painter.setPen(QtCore.Qt.PenStyle.NoPen)
painter.setBrush(QtCore.Qt.GlobalColor.black) # Черный для маски
painter.setBrush(QtCore.Qt.GlobalColor.black) # Black for mask
# Рисуем закругленный прямоугольник на увеличенном разрешении
# Draw rounded rectangle at increased resolution
scaled_rect = QtCore.QRectF(0, 0, scaled_size.width(), scaled_size.height())
scaled_radius = self.CORNER_RADIUS * scale_factor
painter.drawRoundedRect(scaled_rect, scaled_radius, scaled_radius)
painter.end()
# Масштабируем обратно с сглаживанием
# Scale back with smoothing
pixmap = pixmap.scaled(
size,
QtCore.Qt.AspectRatioMode.IgnoreAspectRatio,
QtCore.Qt.TransformationMode.SmoothTransformation
)
# Преобразуем в QBitmap для маски
# В маске: непрозрачные пиксели = видимые области, прозрачные = невидимые
# Convert to QBitmap for mask
# In mask: opaque pixels = visible areas, transparent = invisible
image = pixmap.toImage()
# Создаем маску из непрозрачных пикселей
# Create mask from opaque pixels
bitmap = QtGui.QBitmap.fromImage(image.createAlphaMask())
# Применяем маску
# Apply mask
self.setMask(bitmap)
def add_widget(self, widget: QtWidgets.QWidget) -> QtWidgets.QWidget:
@ -172,18 +172,18 @@ class FloatingMenu(QtWidgets.QWidget):
self._cached_viewport_size = None
self._cached_window_pos = None
self.update_position()
# Запускаем таймер для отслеживания перемещения окна
# Start timer for tracking window movement
self._position_timer.start()
# Ensure menu is on top after positioning
self.raise_()
# Возвращаем фокус view, чтобы события клавиатуры обрабатывались правильно
# FloatingMenu не должен перехватывать фокус, так как он имеет NoFocus
# Return focus to view so keyboard events are handled correctly
# FloatingMenu should not intercept focus as it has NoFocus
if self.view:
self.view.setFocus()
def hide_menu(self) -> None:
self.current_item = None
# Останавливаем таймер
# Stop timer
self._position_timer.stop()
# Clear cache when hiding menu
self._cached_position = None
@ -207,7 +207,7 @@ class FloatingMenu(QtWidgets.QWidget):
view_rect = viewport.rect()
viewport_size = view_rect.size()
# Получаем текущую позицию viewport для проверки изменений
# Get current viewport position for change detection
current_viewport_pos = viewport.mapToGlobal(view_rect.topLeft())
# Check if viewport size and position changed
@ -232,28 +232,28 @@ class FloatingMenu(QtWidgets.QWidget):
bottom_right_global = viewport.mapToGlobal(view_rect.bottomRight())
top_right_global = viewport.mapToGlobal(view_rect.topRight())
# Получаем границы viewport в глобальных координатах
# Get viewport bounds in global coordinates
viewport_left = top_left_global.x()
viewport_right = top_right_global.x()
viewport_top = top_left_global.y()
viewport_bottom = bottom_left_global.y()
viewport_width = viewport_right - viewport_left
# Вычисляем позицию по X (центрируем, но не выходим за границы)
# Calculate X position (center, but don't go beyond boundaries)
x = viewport_left + max(0, (viewport_width - width) // 2)
# Ограничиваем, чтобы меню не выходило за левую и правую границы
# Limit to prevent menu from going beyond left and right boundaries
x = max(viewport_left, min(x, viewport_right - width))
# Вычисляем позицию по Y (снизу с отступом)
# Calculate Y position (bottom with margin)
y = viewport_bottom - height - self.BOTTOM_MARGIN
# Ограничиваем, чтобы меню не выходило за верхнюю границу
# Если меню не помещается снизу, размещаем его сверху
# Limit to prevent menu from going beyond top boundary
# If menu doesn't fit at bottom, place it at top
if y < viewport_top:
y = viewport_top + self.BOTTOM_MARGIN
# Также проверяем, что меню не выходит за нижнюю границу
# Also check that menu doesn't go beyond bottom boundary
if y + height > viewport_bottom:
y = viewport_bottom - height - self.BOTTOM_MARGIN
# Если и так не помещается, размещаем сверху
# If still doesn't fit, place at top
if y < viewport_top:
y = viewport_top + self.BOTTOM_MARGIN
@ -265,7 +265,7 @@ class FloatingMenu(QtWidgets.QWidget):
self._cached_position = new_position
self._cached_viewport_size = viewport_size
self._cached_window_pos = current_viewport_pos
# Обновляем маску после изменения размера/позиции
# Update mask after size/position change
self._apply_rounded_mask()
def parentWidget(self):
@ -289,6 +289,6 @@ class FloatingMenu(QtWidgets.QWidget):
super().mouseReleaseEvent(event)
def resizeEvent(self, event: QtGui.QResizeEvent) -> None:
"""Обновляет маску при изменении размера виджета."""
"""Updates mask when widget size changes."""
super().resizeEvent(event)
self._apply_rounded_mask()