Fix QMenuBar tools buttons rendering using child widget overlay

- Исправлено отображение кнопок инструментов в QMenuBar: вместо QWidgetAction использован дочерний виджет QWidget с абсолютным позиционированием.
- Добавлено позиционирование виджета кнопок справа от заголовка Инструменты с автоматическим пересчетом координат в resizeEvent.
- Обновлен юнит-тест test_menu_bar_tools_buttons под новое представление.
This commit is contained in:
Данил Ярош 2026-07-31 16:58:30 +03:00
parent 3431ef6b50
commit 5507d1c7c7
2 changed files with 37 additions and 18 deletions

View File

@ -3,7 +3,7 @@ from copy import deepcopy
from pathlib import Path
import sys
from PySide6.QtCore import QEvent, QSignalBlocker, QSize, Qt
from PySide6.QtCore import QEvent, QSignalBlocker, QSize, Qt, QTimer
from PySide6.QtGui import QAction, QColor, QImageReader, QKeySequence
from PySide6.QtWidgets import (
QAbstractItemView,
@ -224,6 +224,7 @@ class MainWindow(QMainWindow):
self.sidebar_tab_container: SidebarTab | None = None
self._sidebar_collapsed = False
self._sidebar_last_width = SIDEBAR_DEFAULT_WIDTH
self.menu_buttons_widget: QWidget | None = None
self.canvas = LedCanvas()
self._apply_canvas_settings()
@ -493,23 +494,28 @@ 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)
# Delete old widget if recreating
if hasattr(self, "menu_buttons_widget") and self.menu_buttons_widget is not None:
self.menu_buttons_widget.deleteLater()
self.menu_buttons_widget = None
# Add vertical separator and buttons next to the menu headers in the QMenuBar as a child overlay
self.menu_buttons_widget = QWidget(menu_bar)
layout = QHBoxLayout(self.menu_buttons_widget)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(4)
# Vertical line separator
separator = QFrame()
separator = QFrame(self.menu_buttons_widget)
separator.setFrameShape(QFrame.Shape.VLine)
separator.setFrameShadow(QFrame.Shadow.Sunken)
separator.setStyleSheet("color: #cbd5e1; margin: 4px 0;")
separator.setStyleSheet("color: #cbd5e1; margin: 2px 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 = QToolButton(self.menu_buttons_widget)
btn.setDefaultAction(action)
btn.setIconSize(QSize(16, 16))
btn.setToolButtonStyle(Qt.ToolButtonIconOnly)
@ -517,9 +523,26 @@ class MainWindow(QMainWindow):
btn.setStyleSheet("QToolButton { border: none; padding: 2px; }")
layout.addWidget(btn)
widget_action = QWidgetAction(menu_bar)
widget_action.setDefaultWidget(container)
menu_bar.addAction(widget_action)
self.menu_buttons_widget.show()
QTimer.singleShot(0, self._reposition_menu_buttons)
def resizeEvent(self, event) -> None:
super().resizeEvent(event)
self._reposition_menu_buttons()
def _reposition_menu_buttons(self) -> None:
if not hasattr(self, "menu_buttons_widget") or self.menu_buttons_widget is None:
return
menu_bar = self.menuBar()
actions = menu_bar.actions()
if len(actions) < 4:
return
tools_action = actions[3]
rect = menu_bar.actionGeometry(tools_action)
if rect.isValid() and rect.right() > 0:
h = menu_bar.height()
w = self.menu_buttons_widget.sizeHint().width()
self.menu_buttons_widget.setGeometry(rect.right() + 10, (h - 20) // 2, w, 20)
def _build_layout(self) -> None:
side = QWidget()

View File

@ -1194,14 +1194,10 @@ def test_menu_bar_tools_buttons() -> None:
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()
from PySide6.QtWidgets import QToolButton, QFrame
container = window.menu_buttons_widget
assert container is not None
assert container.parent() == window.menuBar()
# Check layout
layout = container.layout()