Release icon and connection direction update
This commit is contained in:
parent
59bbad8288
commit
ab46e603b9
@ -1,12 +1,24 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
spec_dir = Path(SPECPATH)
|
||||
|
||||
|
||||
a = Analysis(
|
||||
['main.py'],
|
||||
pathex=['.'],
|
||||
[str(spec_dir / 'main.py')],
|
||||
pathex=[str(spec_dir), str(spec_dir.parent)],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
hiddenimports=[],
|
||||
hiddenimports=[
|
||||
'canvas',
|
||||
'icons',
|
||||
'main_window',
|
||||
'models',
|
||||
'panel_mapping_generator',
|
||||
'unfolding',
|
||||
],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
@ -35,4 +47,5 @@ exe = EXE(
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon=str(spec_dir / 'assets' / 'ledmapper.ico'),
|
||||
)
|
||||
|
||||
BIN
assets/ledmapper.ico
Normal file
BIN
assets/ledmapper.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 264 KiB |
36
canvas.py
36
canvas.py
@ -529,15 +529,21 @@ class LedCanvas(QGraphicsView):
|
||||
inner_alpha = 0.9 if is_active else 0.25
|
||||
bridge_width = 1.9 if is_active else 1.0
|
||||
bridge_alpha = 0.65 if is_active else 0.18
|
||||
arrow_width = 1.8 if is_active else 1.0
|
||||
arrow_alpha = 0.95 if is_active else 0.28
|
||||
inner_path = QPainterPath()
|
||||
inner_arrows = QPainterPath()
|
||||
for block in line.blocks:
|
||||
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())
|
||||
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)
|
||||
|
||||
previous_block_end = None
|
||||
bridge_path = QPainterPath()
|
||||
bridge_arrows = QPainterPath()
|
||||
for block in line.blocks:
|
||||
if not block.point_ids:
|
||||
continue
|
||||
@ -545,22 +551,48 @@ 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 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)
|
||||
|
||||
@staticmethod
|
||||
def _append_connection(path: QPainterPath, start: QPointF, end: QPointF) -> None:
|
||||
path.moveTo(start)
|
||||
path.lineTo(end)
|
||||
|
||||
def _add_connection_path(self, path: QPainterPath, color: str, width: float, alpha: float) -> None:
|
||||
@staticmethod
|
||||
def _append_arrow_marker(path: QPainterPath, start: QPointF, end: QPointF) -> None:
|
||||
dx = end.x() - start.x()
|
||||
dy = end.y() - start.y()
|
||||
length = math.hypot(dx, dy)
|
||||
if length < 8.0:
|
||||
return
|
||||
unit_x = dx / length
|
||||
unit_y = dy / length
|
||||
mid_x = start.x() + dx * 0.55
|
||||
mid_y = start.y() + dy * 0.55
|
||||
arrow_length = min(7.0, length * 0.28)
|
||||
arrow_width = arrow_length * 0.55
|
||||
base_x = mid_x - unit_x * arrow_length
|
||||
base_y = mid_y - unit_y * arrow_length
|
||||
perp_x = -unit_y
|
||||
perp_y = unit_x
|
||||
left = QPointF(base_x + perp_x * arrow_width, base_y + perp_y * arrow_width)
|
||||
right = QPointF(base_x - perp_x * arrow_width, base_y - perp_y * arrow_width)
|
||||
tip = QPointF(mid_x, mid_y)
|
||||
path.moveTo(left)
|
||||
path.lineTo(tip)
|
||||
path.lineTo(right)
|
||||
|
||||
def _add_connection_path(self, path: QPainterPath, color: str, width: float, alpha: float, *, z_value: float = 1) -> None:
|
||||
if path.isEmpty():
|
||||
return
|
||||
pen_color = QColor(color)
|
||||
pen_color.setAlphaF(alpha)
|
||||
item = self.scene().addPath(path, QPen(pen_color, width))
|
||||
item.setZValue(1)
|
||||
item.setZValue(z_value)
|
||||
self.connection_items.append(item)
|
||||
|
||||
def _selection_has_assignment(self) -> bool:
|
||||
|
||||
1
icons.py
1
icons.py
@ -14,6 +14,7 @@ LUCIDE_PATHS = {
|
||||
"file-plus": '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z"/><path d="M14 2v6h6"/><path d="M12 18v-6"/><path d="M9 15h6"/>',
|
||||
"file-code": '<path d="M14 2v6h6"/><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z"/><path d="m10 13-2 2 2 2"/><path d="m14 17 2-2-2-2"/>',
|
||||
"folder-open": '<path d="m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6A2 2 0 0 1 18.46 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4.9a2 2 0 0 1 1.69.9L12 6h8a2 2 0 0 1 2 2v2"/><path d="M2 13h4"/>',
|
||||
"git-branch": '<line x1="6" x2="6" y1="3" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/>',
|
||||
"grid-2x2": '<rect width="18" height="18" x="3" y="3" rx="2"/><path d="M3 12h18"/><path d="M12 3v18"/>',
|
||||
"layers": '<path d="m12.83 2.18 8.34 4.66a1 1 0 0 1 0 1.74l-8.34 4.66a1.7 1.7 0 0 1-1.66 0L2.83 8.58a1 1 0 0 1 0-1.74l8.34-4.66a1.7 1.7 0 0 1 1.66 0Z"/><path d="m22 12.5-9.17 5.12a1.7 1.7 0 0 1-1.66 0L2 12.5"/><path d="m22 17.5-9.17 5.12a1.7 1.7 0 0 1-1.66 0L2 17.5"/>',
|
||||
"list-plus": '<path d="M11 12H3"/><path d="M16 6H3"/><path d="M16 18H3"/><path d="M18 9v6"/><path d="M21 12h-6"/>',
|
||||
|
||||
15
main.py
15
main.py
@ -1,15 +1,28 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
from main_window import MainWindow
|
||||
APP_DIR = Path(__file__).resolve().parent
|
||||
if str(APP_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(APP_DIR))
|
||||
|
||||
try:
|
||||
from .icons import lucide_icon
|
||||
from .main_window import MainWindow
|
||||
except ImportError:
|
||||
from icons import lucide_icon
|
||||
from main_window import MainWindow
|
||||
|
||||
|
||||
def main() -> int:
|
||||
app = QApplication(sys.argv)
|
||||
app_icon = lucide_icon("git-branch", "#e5e7eb", 256)
|
||||
app.setWindowIcon(app_icon)
|
||||
window = MainWindow()
|
||||
window.setWindowIcon(app_icon)
|
||||
window.show()
|
||||
return app.exec()
|
||||
|
||||
|
||||
29
qa_smoke.py
29
qa_smoke.py
@ -81,6 +81,7 @@ def main() -> None:
|
||||
test_canvas_rebuild_can_preserve_user_zoom,
|
||||
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_rebuild_drops_deleted_selection_items,
|
||||
test_canvas_refresh_does_not_accumulate_connection_items,
|
||||
test_main_window_has_classic_menu_bar,
|
||||
@ -767,6 +768,32 @@ def test_canvas_connects_new_block_to_previous_non_empty_block() -> None:
|
||||
assert bridge_found
|
||||
|
||||
|
||||
def test_canvas_draws_direction_arrows_on_connections() -> 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.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)
|
||||
|
||||
arrow_paths = [item.path() for item in canvas.connection_items if item.zValue() == 2]
|
||||
assert arrow_paths
|
||||
assert sum(path.elementCount() for path in arrow_paths) >= 6
|
||||
|
||||
|
||||
def test_canvas_rebuild_drops_deleted_selection_items() -> None:
|
||||
if LedCanvas is None or QApplication is None:
|
||||
return
|
||||
@ -963,6 +990,7 @@ def test_lucide_icon_names_are_declared() -> None:
|
||||
"file-plus",
|
||||
"file-code",
|
||||
"folder-open",
|
||||
"git-branch",
|
||||
"grid-2x2",
|
||||
"layers",
|
||||
"paintbrush",
|
||||
@ -1356,6 +1384,7 @@ def test_main_window_restores_tree_state_after_refresh_error() -> None:
|
||||
|
||||
|
||||
def test_package_imports_work_from_project_root() -> None:
|
||||
importlib.import_module("app.main")
|
||||
importlib.import_module("app.canvas")
|
||||
importlib.import_module("app.main_window")
|
||||
|
||||
|
||||
@ -1 +1,2 @@
|
||||
PySide6>=6.7
|
||||
PySide6==6.11.1
|
||||
pyinstaller==6.20.0
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user