Commit 4f407bb8 authored by Samuel GAIST's avatar Samuel GAIST
Browse files

[widgets][toolchaineditor] Implement basic toolchain preview

This allows to visualise what a toolchain should look like
parent 755ac253
......@@ -23,15 +23,42 @@
# #
###############################################################################
import pytest
from ..backend.asset import Asset
from ..backend.asset import AssetType
from ..backend.assetmodel import AssetModel
from ..widgets.toolchaineditor import ToolchainEditor
from .conftest import sync_prefix
from .conftest import prefix
def get_toolchain_declaration(prefix_path, asset_name):
asset = Asset(prefix_path, AssetType.TOOLCHAIN, asset_name)
return asset.declaration
def get_valid_toolchains(test_prefix):
sync_prefix()
model = AssetModel()
model.asset_type = AssetType.TOOLCHAIN
model.prefix_path = test_prefix
model.setLatestOnlyEnabled(False)
return [
toolchain
for toolchain in model.stringList()
if all(invalid not in toolchain for invalid in ["errors"])
]
class TestToolchainEditor:
"""Test that the mock editor works correctly"""
def test_load_and_dump(self, qtbot):
reference_json = {"description": "test"}
@pytest.mark.parametrize("toolchain", get_valid_toolchains(prefix))
def test_load_and_dump(self, qtbot, test_prefix, toolchain):
reference_json = get_toolchain_declaration(test_prefix, toolchain)
editor = ToolchainEditor()
editor.load_json(reference_json)
......
......@@ -23,12 +23,141 @@
# #
###############################################################################
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QImage
from PyQt5.QtGui import QPixmap
from PyQt5.QtGui import QPalette
from PyQt5.QtWidgets import QLabel
from PyQt5.QtWidgets import QScrollArea
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import QWidget
from graphviz import Digraph
from itertools import zip_longest
from ..backend.asset import AssetType
from ..decorators import frozen
from .editor import AbstractAssetEditor
class SimpleToolchainPreview(QWidget):
"""Basic toolchain preview"""
def __init__(self, parent=None):
super().__init__(parent=parent)
self.json_object = {}
self.label = QLabel()
self.label.setBackgroundRole(QPalette.Base)
scrollarea = QScrollArea()
scrollarea.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
scrollarea.setWidget(self.label)
layout = QVBoxLayout(self)
layout.addWidget(scrollarea)
def load(self, json_object):
"""Parse the json in parameter and generates a graph"""
self.json_object = json_object
graph = Digraph("toolchain", format="png")
graph.attr(rankdir="LR")
graph.node_attr["shape"] = "plaintext"
def build_rows(inputs, outputs):
input_cell = "<td bgcolor='#ffff00' port='in_{input}'>{input}</td>"
output_cell = "<td bgcolor='#00ffff' port='out_{output}'>{output}</td>"
empty_cell = "<td border='0'></td>"
rows = ""
if inputs and outputs:
for input_, output in zip_longest(inputs, outputs):
rows += "<tr>"
if input_:
rows += input_cell.format(input=input_)
else:
rows += empty_cell
if output:
rows += output_cell.format(output=output)
else:
rows += empty_cell
rows += "</tr>"
elif inputs:
for input_ in inputs:
rows += "<tr>"
rows += input_cell.format(input=input_)
rows += "</tr>"
elif outputs:
for output in outputs:
rows += "<tr>"
rows += output_cell.format(output=output)
rows += "</tr>"
return rows
def build_block_table(block, background):
block_name = block["name"]
inputs = block.get("inputs", {})
outputs = block.get("outputs", {})
columns = 2 if inputs and outputs else 1
label = f"<<table border='1' cellborder='1' bgcolor='{background}'>"
label += f"<tr><td colspan='{columns}' border='0'>{block_name}</td></tr>"
label += build_rows(inputs, outputs)
label += "</table>>"
return block_name, label
DATASET_COLOR = "#fffd85"
BLOCK_COLOR = "#d4d4cd"
ANALYZER_COLOR = "#8cecf5"
for block in json_object["datasets"]:
name, label = build_block_table(block, DATASET_COLOR)
graph.node(name=name, label=label)
for block in json_object["blocks"]:
name, label = build_block_table(block, BLOCK_COLOR)
graph.node(name=name, label=label)
for block in json_object.get("loops", []):
block_name = block["name"]
label = f"<<table border='1' cellborder='1' bgcolor='{BLOCK_COLOR}'>"
label += f"<tr><td colspan='2' border='0'>{block_name}</td></tr>"
for prefix in ["processor", "evaluator"]:
label += "<tr><td><table border='0' cellborder='1' bgcolor='#def2a7'>"
label += f"<tr><td colspan='2' border='0'>{prefix}</td></tr>"
label += build_rows(
block.get(f"{prefix}_inputs", {}),
block.get(f"{prefix}_outputs", {}),
)
label += "</table></td></tr>"
label += "</table>>"
graph.node(block_name, label=label)
for block in json_object["analyzers"]:
name, label = build_block_table(block, ANALYZER_COLOR)
graph.node(name=name, label=label)
for connection in json_object["connections"]:
from_block, output = connection["from"].split(".")
to_block, input_ = connection["to"].split(".")
graph.edge(f"{from_block}:out_{output}:e", f"{to_block}:in_{input_}:w")
self.label.setPixmap(QPixmap.fromImage(QImage.fromData(graph.pipe())))
self.label.adjustSize()
def dump(self):
"""Returns the json used to load the widget"""
return self.json_object
@frozen
class ToolchainEditor(AbstractAssetEditor):
def __init__(self, parent=None):
......@@ -36,10 +165,16 @@ class ToolchainEditor(AbstractAssetEditor):
self.setObjectName(self.__class__.__name__)
self.set_title(self.tr("Toolchain"))
self.preview = SimpleToolchainPreview()
self.layout().addWidget(self.preview, 2)
self.layout().addStretch()
def _load_json(self, json_object):
"""Load the json object passed as parameter"""
pass
self.preview.load(json_object)
def _dump_json(self):
"""Returns the json representation of the asset"""
return {}
return self.preview.dump()
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment