From 3431ef6b509b2cab5722bed98929d0b481a08f4f Mon Sep 17 00:00:00 2001 From: Danil Iarosh Date: Fri, 31 Jul 2026 16:32:24 +0300 Subject: [PATCH] Add Tools menu buttons directly to QMenuBar next to menu headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Инструменты добавлены непосредственно на панель меню QMenuBar в виде QToolButton. - Действия инструментов отображаются справа от заголовков меню (Файл, Правка, Вид, Инструменты). - Действия инструментов отделены от заголовков вертикальным разделителем. - Добавлен и пройден тест test_menu_bar_tools_buttons в qa_smoke.py. --- main_window.py | 30 ++++++++++++++++++++++++++++++ qa_smoke.py | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/main_window.py b/main_window.py index 6b0940d..90af296 100644 --- a/main_window.py +++ b/main_window.py @@ -37,6 +37,8 @@ from PySide6.QtWidgets import ( QLineEdit, QPlainTextEdit, QTextEdit, + QFrame, + QWidgetAction, ) try: @@ -491,6 +493,34 @@ class MainWindow(QMainWindow): tools_menu.addSeparator() tools_menu.addAction(self.actions["brush"]) + # Add vertical separator and buttons next to the menu headers in the QMenuBar + container = QWidget() + layout = QHBoxLayout(container) + layout.setContentsMargins(10, 0, 10, 0) + layout.setSpacing(4) + + # Vertical line separator + separator = QFrame() + separator.setFrameShape(QFrame.Shape.VLine) + separator.setFrameShadow(QFrame.Shadow.Sunken) + separator.setStyleSheet("color: #cbd5e1; margin: 4px 0;") + layout.addWidget(separator) + + # Add Tools actions as buttons + for key in ["add_line", "add_led", "generate_script", "auto_blocks", "counterclockwise", "brush"]: + action = self.actions[key] + btn = QToolButton() + btn.setDefaultAction(action) + btn.setIconSize(QSize(16, 16)) + btn.setToolButtonStyle(Qt.ToolButtonIconOnly) + btn.setAutoRaise(True) + btn.setStyleSheet("QToolButton { border: none; padding: 2px; }") + layout.addWidget(btn) + + widget_action = QWidgetAction(menu_bar) + widget_action.setDefaultWidget(container) + menu_bar.addAction(widget_action) + def _build_layout(self) -> None: side = QWidget() self.side_panel = side diff --git a/qa_smoke.py b/qa_smoke.py index 23a83ee..6256168 100644 --- a/qa_smoke.py +++ b/qa_smoke.py @@ -130,6 +130,7 @@ def main() -> None: test_block_renaming_and_naming_strategy, test_line_renaming_and_tree_shortcuts, test_new_file_action, + test_menu_bar_tools_buttons, test_export_mapping_to_image, test_save_preserves_unknown_source_fields, test_ordered_json_preserves_document_and_remaps_uids, @@ -1186,6 +1187,41 @@ def test_new_file_action() -> None: +def test_menu_bar_tools_buttons() -> None: + if MainWindow is None or QApplication is None: + return + app = QApplication.instance() or QApplication([]) + window = MainWindow() + app.processEvents() + + # Find the QWidgetAction in the QMenuBar + from PySide6.QtWidgets import QWidgetAction, QToolButton, QFrame + menu_bar = window.menuBar() + widget_actions = [action for action in menu_bar.actions() if isinstance(action, QWidgetAction)] + assert len(widget_actions) == 1 + + container = widget_actions[0].defaultWidget() + assert container is not None + + # Check layout + layout = container.layout() + assert layout is not None + + # Find separator and buttons + frames = container.findChildren(QFrame) + assert len(frames) == 1 + + buttons = container.findChildren(QToolButton) + assert len(buttons) == 6 + + button_actions = [btn.defaultAction() for btn in buttons] + expected_keys = ["add_line", "add_led", "generate_script", "auto_blocks", "counterclockwise", "brush"] + for key in expected_keys: + assert window.actions[key] in button_actions + + window.close() + + def test_export_mapping_to_image() -> None: if QApplication is None: return @@ -1935,7 +1971,7 @@ def test_classic_menu_contains_expected_actions() -> None: app.processEvents() expected = { - "Файл": ["Создать...", "Открыть", "Сохранить", "Сохранить как", "Сгенерировать скрипт", "Параметры"], + "Файл": ["Создать...", "Открыть", "Сохранить", "Сохранить как", "Экспортировать в изображение...", "Сгенерировать скрипт", "Параметры"], "Правка": ["Отменить", "Повторить", "Удалить фиксацию", "Удалить точки", "Удалить все", "Очистить выбор"], "Вид": [ "Показать всё",