Release build naming and arrow visibility controls
This commit is contained in:
parent
5895ccc29a
commit
bbf580bb56
@ -1,11 +1,41 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
spec_dir = Path(SPECPATH)
|
||||
|
||||
|
||||
def git_build_ref():
|
||||
try:
|
||||
tag = subprocess.check_output(
|
||||
['git', 'describe', '--tags', '--exact-match', 'HEAD'],
|
||||
cwd=spec_dir,
|
||||
stderr=subprocess.DEVNULL,
|
||||
text=True,
|
||||
).strip()
|
||||
if tag:
|
||||
return tag
|
||||
except (OSError, subprocess.CalledProcessError):
|
||||
pass
|
||||
try:
|
||||
commit = subprocess.check_output(
|
||||
['git', 'rev-parse', '--short', 'HEAD'],
|
||||
cwd=spec_dir,
|
||||
stderr=subprocess.DEVNULL,
|
||||
text=True,
|
||||
).strip()
|
||||
if commit:
|
||||
return commit
|
||||
except (OSError, subprocess.CalledProcessError):
|
||||
pass
|
||||
return 'untagged'
|
||||
|
||||
|
||||
app_name = f'LEDMapper-{git_build_ref()}'
|
||||
|
||||
|
||||
a = Analysis(
|
||||
[str(spec_dir / 'main.py')],
|
||||
pathex=[str(spec_dir), str(spec_dir.parent)],
|
||||
@ -34,7 +64,7 @@ exe = EXE(
|
||||
a.binaries,
|
||||
a.datas,
|
||||
[],
|
||||
name='LEDMapper',
|
||||
name=app_name,
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
|
||||
@ -100,6 +100,7 @@ python -m venv .venv
|
||||
| Очистить выбор | `Esc` |
|
||||
| Показать всю схему | `Ctrl+0` |
|
||||
| Сетка 34 мм | `G` |
|
||||
| Стрелки направления | `Ctrl+Shift+A` |
|
||||
| Скрыть/показать боковую панель | `F9` |
|
||||
| Новая линия | `Ctrl+L` |
|
||||
| Автоблоки | `Ctrl+Shift+B` |
|
||||
|
||||
22
canvas.py
22
canvas.py
@ -66,6 +66,7 @@ class LedCanvas(QGraphicsView):
|
||||
self.items_by_id: dict[int, LedItem] = {}
|
||||
self.grid_items: list[QGraphicsLineItem] = []
|
||||
self.connection_items: list[QGraphicsPathItem] = []
|
||||
self._direction_arrows_visible = True
|
||||
self._point_radius = 4.5
|
||||
self._brush_radius = 18.0
|
||||
self._brush_zone_item: QGraphicsEllipseItem | None = None
|
||||
@ -100,6 +101,15 @@ class LedCanvas(QGraphicsView):
|
||||
self._grid_visible = visible
|
||||
self._refresh_grid()
|
||||
|
||||
def set_direction_arrows_visible(self, visible: bool) -> None:
|
||||
if self._direction_arrows_visible == visible:
|
||||
return
|
||||
self._direction_arrows_visible = visible
|
||||
self.refresh_styles(redraw_connections=True)
|
||||
|
||||
def direction_arrows_visible(self) -> bool:
|
||||
return self._direction_arrows_visible
|
||||
|
||||
def set_brush_enabled(self, enabled: bool) -> None:
|
||||
self._brush_enabled = enabled
|
||||
self._is_brush_stroke = False
|
||||
@ -537,9 +547,11 @@ class LedCanvas(QGraphicsView):
|
||||
ordered_items = [self.items_by_id[point_id] for point_id in block.point_ids if point_id in self.items_by_id]
|
||||
for first, second in zip(ordered_items, ordered_items[1:]):
|
||||
self._append_connection(inner_path, first.pos(), second.pos())
|
||||
self._append_arrow_marker(inner_arrows, first.pos(), second.pos())
|
||||
if self._direction_arrows_visible:
|
||||
self._append_arrow_marker(inner_arrows, first.pos(), second.pos())
|
||||
self._add_connection_path(inner_path, line.color, inner_width, inner_alpha)
|
||||
self._add_connection_path(inner_arrows, line.color, arrow_width, arrow_alpha, z_value=2)
|
||||
if self._direction_arrows_visible:
|
||||
self._add_connection_path(inner_arrows, line.color, arrow_width, arrow_alpha, z_value=2)
|
||||
|
||||
previous_block_end = None
|
||||
bridge_path = QPainterPath()
|
||||
@ -551,11 +563,13 @@ class LedCanvas(QGraphicsView):
|
||||
block_end = self.items_by_id.get(block.point_ids[-1])
|
||||
if block_start is not None and previous_block_end is not None:
|
||||
self._append_connection(bridge_path, previous_block_end.pos(), block_start.pos())
|
||||
self._append_arrow_marker(bridge_arrows, previous_block_end.pos(), block_start.pos())
|
||||
if self._direction_arrows_visible:
|
||||
self._append_arrow_marker(bridge_arrows, previous_block_end.pos(), block_start.pos())
|
||||
if block_end is not None:
|
||||
previous_block_end = block_end
|
||||
self._add_connection_path(bridge_path, line.color, bridge_width, bridge_alpha)
|
||||
self._add_connection_path(bridge_arrows, line.color, arrow_width, bridge_alpha, z_value=2)
|
||||
if self._direction_arrows_visible:
|
||||
self._add_connection_path(bridge_arrows, line.color, arrow_width, bridge_alpha, z_value=2)
|
||||
|
||||
@staticmethod
|
||||
def _append_connection(path: QPainterPath, start: QPointF, end: QPointF) -> None:
|
||||
|
||||
1
icons.py
1
icons.py
@ -7,6 +7,7 @@ from PySide6.QtSvg import QSvgRenderer
|
||||
|
||||
LUCIDE_PATHS = {
|
||||
"archive-restore": '<rect width="20" height="5" x="2" y="3" rx="1"/><path d="M4 8v11a2 2 0 0 0 2 2h2"/><path d="M20 8v11a2 2 0 0 1-2 2h-2"/><path d="m9 15 3-3 3 3"/><path d="M12 12v9"/>',
|
||||
"arrow-right": '<path d="M5 12h14"/><path d="m12 5 7 7-7 7"/>',
|
||||
"brush": '<path d="m9.06 11.9 8.07-8.06a2.85 2.85 0 1 1 4.03 4.03l-8.06 8.08"/><path d="M7.07 14.94c-1.66 0-3 1.35-3 3.01 0 1.1-.89 2-2 2 1.4 1.4 3.67 1.4 5.07 0l2.94-2.94a2.93 2.93 0 0 0-4.14-4.14z"/>',
|
||||
"chevron-right": '<path d="m9 18 6-6-6-6"/>',
|
||||
"eraser": '<path d="m7 21-4.3-4.3c-.9-.9-.9-2.4 0-3.3L14 2l8 8-11 11Z"/><path d="M22 21H7"/><path d="m5 11 8 8"/>',
|
||||
|
||||
@ -209,6 +209,16 @@ class MainWindow(QMainWindow):
|
||||
add_action("fit_view", "Показать всё", self.canvas.fit_to_content, "eye", QKeySequence("Ctrl+0"))
|
||||
grid_action = add_action("grid", "Сетка 34 мм", self.toggle_grid, "grid-2x2", QKeySequence("G"), "Показать или скрыть сетку 34 мм")
|
||||
grid_action.setCheckable(True)
|
||||
arrows_action = add_action(
|
||||
"direction_arrows",
|
||||
"Стрелки направления",
|
||||
self.toggle_direction_arrows,
|
||||
"arrow-right",
|
||||
QKeySequence("Ctrl+Shift+A"),
|
||||
"Показать или скрыть стрелки направления на линиях",
|
||||
)
|
||||
arrows_action.setCheckable(True)
|
||||
arrows_action.setChecked(True)
|
||||
add_action("toggle_sidebar", "Скрыть боковую панель", self.toggle_sidebar, "chevron-right", QKeySequence("F9"))
|
||||
self._build_menu_bar()
|
||||
self.addAction(self.delete_action)
|
||||
@ -234,6 +244,7 @@ class MainWindow(QMainWindow):
|
||||
view_menu = menu_bar.addMenu("Вид")
|
||||
view_menu.addAction(self.actions["fit_view"])
|
||||
view_menu.addAction(self.actions["grid"])
|
||||
view_menu.addAction(self.actions["direction_arrows"])
|
||||
|
||||
window_menu = menu_bar.addMenu("Окно")
|
||||
window_menu.addAction(self.actions["toggle_sidebar"])
|
||||
@ -349,6 +360,11 @@ class MainWindow(QMainWindow):
|
||||
else:
|
||||
self.collapse_sidebar()
|
||||
|
||||
def toggle_direction_arrows(self) -> None:
|
||||
action = self.actions.get("direction_arrows")
|
||||
visible = True if action is None else action.isChecked()
|
||||
self.canvas.set_direction_arrows_visible(visible)
|
||||
|
||||
def collapse_sidebar(self) -> None:
|
||||
if self.sidebar_splitter is None or self.sidebar_stack is None or self._sidebar_collapsed:
|
||||
return
|
||||
|
||||
59
qa_smoke.py
59
qa_smoke.py
@ -82,6 +82,7 @@ def main() -> None:
|
||||
test_grid_toggle_adds_grid_items,
|
||||
test_canvas_connects_new_block_to_previous_non_empty_block,
|
||||
test_canvas_draws_direction_arrows_on_connections,
|
||||
test_canvas_can_toggle_direction_arrows,
|
||||
test_canvas_rebuild_drops_deleted_selection_items,
|
||||
test_canvas_refresh_does_not_accumulate_connection_items,
|
||||
test_main_window_has_classic_menu_bar,
|
||||
@ -90,6 +91,7 @@ def main() -> None:
|
||||
test_undo_redo_roundtrip_for_line_creation,
|
||||
test_main_window_actions_use_lucide_icons,
|
||||
test_main_window_action_shortcuts_are_assigned,
|
||||
test_main_window_direction_arrow_action_toggles_canvas,
|
||||
test_lucide_icon_names_are_declared,
|
||||
test_table_hides_empty_line_index_columns,
|
||||
test_sidebar_collapses_and_expands,
|
||||
@ -794,6 +796,37 @@ def test_canvas_draws_direction_arrows_on_connections() -> None:
|
||||
assert sum(path.elementCount() for path in arrow_paths) >= 6
|
||||
|
||||
|
||||
def test_canvas_can_toggle_direction_arrows() -> None:
|
||||
if LedCanvas is None or QApplication is None:
|
||||
return
|
||||
app = QApplication.instance() or QApplication([])
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
path = Path(temp_dir) / "arrows-toggle.json"
|
||||
points = [
|
||||
{"id": 0, "x": 0.0, "y": 0.0, "row": 0, "col": 0},
|
||||
{"id": 1, "x": 34.0, "y": 0.0, "row": 0, "col": 1},
|
||||
{"id": 2, "x": 68.0, "y": 0.0, "row": 0, "col": 2},
|
||||
]
|
||||
path.write_text(json.dumps({"circles": points}), encoding="utf-8")
|
||||
doc = MappingDocument()
|
||||
doc.load(path)
|
||||
line = doc.add_line()
|
||||
doc.add_block(line.id, [0, 1, 2])
|
||||
|
||||
canvas = LedCanvas()
|
||||
canvas.set_document(doc)
|
||||
canvas.set_active_line(line.id)
|
||||
assert any(item.zValue() == 2 for item in canvas.connection_items)
|
||||
|
||||
canvas.set_direction_arrows_visible(False)
|
||||
assert canvas.direction_arrows_visible() is False
|
||||
assert not any(item.zValue() == 2 for item in canvas.connection_items)
|
||||
|
||||
canvas.set_direction_arrows_visible(True)
|
||||
assert canvas.direction_arrows_visible() is True
|
||||
assert any(item.zValue() == 2 for item in canvas.connection_items)
|
||||
|
||||
|
||||
def test_canvas_rebuild_drops_deleted_selection_items() -> None:
|
||||
if LedCanvas is None or QApplication is None:
|
||||
return
|
||||
@ -855,7 +888,7 @@ def test_classic_menu_contains_expected_actions() -> None:
|
||||
expected = {
|
||||
"Файл": ["Открыть", "Сохранить", "Сохранить как", "Сгенерировать скрипт"],
|
||||
"Правка": ["Отменить", "Повторить", "Удалить фиксацию", "Удалить все", "Очистить выбор"],
|
||||
"Вид": ["Показать всё", "Сетка 34 мм"],
|
||||
"Вид": ["Показать всё", "Сетка 34 мм", "Стрелки направления"],
|
||||
"Окно": ["Скрыть боковую панель"],
|
||||
"Инструменты": ["Новая линия", "Сгенерировать скрипт", "Автоблоки", "Кисть"],
|
||||
}
|
||||
@ -950,6 +983,7 @@ def test_main_window_actions_use_lucide_icons() -> None:
|
||||
"clear_selection",
|
||||
"fit_view",
|
||||
"grid",
|
||||
"direction_arrows",
|
||||
"toggle_sidebar",
|
||||
}
|
||||
missing = [key for key in expected_icon_actions if window.actions[key].icon().isNull()]
|
||||
@ -970,6 +1004,7 @@ def test_main_window_action_shortcuts_are_assigned() -> None:
|
||||
"clear_selection": "Esc",
|
||||
"fit_view": "Ctrl+0",
|
||||
"grid": "G",
|
||||
"direction_arrows": "Ctrl+Shift+A",
|
||||
"toggle_sidebar": "F9",
|
||||
"add_line": "Ctrl+L",
|
||||
"auto_blocks": "Ctrl+Shift+B",
|
||||
@ -981,9 +1016,31 @@ def test_main_window_action_shortcuts_are_assigned() -> None:
|
||||
window.close()
|
||||
|
||||
|
||||
def test_main_window_direction_arrow_action_toggles_canvas() -> None:
|
||||
if MainWindow is None or QApplication is None:
|
||||
return
|
||||
app = QApplication.instance() or QApplication([])
|
||||
window = MainWindow()
|
||||
app.processEvents()
|
||||
|
||||
action = window.actions["direction_arrows"]
|
||||
assert action.isChecked()
|
||||
assert window.canvas.direction_arrows_visible() is True
|
||||
|
||||
action.trigger()
|
||||
assert not action.isChecked()
|
||||
assert window.canvas.direction_arrows_visible() is False
|
||||
|
||||
action.trigger()
|
||||
assert action.isChecked()
|
||||
assert window.canvas.direction_arrows_visible() is True
|
||||
window.close()
|
||||
|
||||
|
||||
def test_lucide_icon_names_are_declared() -> None:
|
||||
expected_names = {
|
||||
"archive-restore",
|
||||
"arrow-right",
|
||||
"chevron-right",
|
||||
"eraser",
|
||||
"eye",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user