diff --git a/b_asic/GUI/arrow.py b/b_asic/GUI/arrow.py index c975c728c8c4a36a1aa208e4f5bfd22107bc9a26..3c931150f3af95f6f2f2d17552c91072e41c4008 100644 --- a/b_asic/GUI/arrow.py +++ b/b_asic/GUI/arrow.py @@ -5,7 +5,7 @@ QGraphicsLineItem, QGraphicsWidget from PySide2.QtCore import Qt, QSize, QLineF, QPoint, QRectF from PySide2.QtGui import QIcon, QFont, QPainter, QPen -from b_asic import Signal +from b_asic import Signal class Arrow(QGraphicsLineItem): @@ -25,6 +25,8 @@ class Arrow(QGraphicsLineItem): menu.exec_(self.cursor().pos()) def remove(self): + self.signal.remove_destination() + self.signal.remove_source() self._window.scene.removeItem(self) self._window.signalList.remove(self) diff --git a/b_asic/GUI/drag_button.py b/b_asic/GUI/drag_button.py index 5b0a734a09c41cd64848dd994ecaaef2d2fa120f..b6ef243ceee127b60ffca58d6833c1d69e2f391e 100644 --- a/b_asic/GUI/drag_button.py +++ b/b_asic/GUI/drag_button.py @@ -20,24 +20,31 @@ class DragButton(QPushButton): moved = Signal() def __init__(self, name, operation, operation_path_name, is_show_name, window, parent = None): self.name = name + self.ports = [] self.is_show_name = is_show_name self._window = window self.operation = operation self.operation_path_name = operation_path_name self.clicked = 0 self.pressed = False + self._m_press = False + self._m_drag = False self._mouse_press_pos = None self._mouse_move_pos = None - super(DragButton, self).__init__(self._window) + super(DragButton, self).__init__(parent) def contextMenuEvent(self, event): menu = QMenu() properties = QAction("Properties") menu.addAction(properties) properties.triggered.connect(self.show_properties_window) + + delete = QAction("Delete") + menu.addAction(delete) + delete.triggered.connect(self.remove) menu.exec_(self.cursor().pos()) - def show_properties_window(self, event): + def show_properties_window(self): self.properties_window = PropertiesWindow(self, self._window) self.properties_window.show() @@ -45,55 +52,86 @@ class DragButton(QPushButton): self.label = label def mousePressEvent(self, event): - if event.button() == Qt.LeftButton: + self._m_press = True self._mouse_press_pos = event.pos() self._mouse_move_pos = event.pos() - for signal in self._window.signalList: - signal.update() - - self.clicked += 1 - if self.clicked == 1: - self.pressed = True - self.setStyleSheet("background-color: grey; border-style: solid;\ - border-color: black; border-width: 2px; border-radius: 10px") - self.setStyleSheet(""" QToolTip { background-color: white; - color: black }""") - path_to_image = os.path.join('operation_icons', self.operation_path_name + '_grey.png') - self.setIcon(QIcon(path_to_image)) - self.setIconSize(QSize(55, 55)) - self._window.pressed_operations.append(self) - - elif self.clicked == 2: - self.clicked = 0 - self.pressed = False - self.setStyleSheet("background-color: white; border-style: solid;\ - border-color: black; border-width: 2px; border-radius: 10px") - self.setStyleSheet(""" QToolTip { background-color: white; - color: black}""") - path_to_image = os.path.join('operation_icons', self.operation_path_name + '.png') - self.setIcon(QIcon(path_to_image)) - self.setIconSize(QSize(55, 55)) - self._window.pressed_operations.remove(self) - super(DragButton, self).mousePressEvent(event) def mouseMoveEvent(self, event): - if event.buttons() == Qt.LeftButton: + if event.buttons() == Qt.LeftButton and self._m_press: + self._m_drag = True self.move(self.mapToParent(event.pos() - self._mouse_press_pos)) + if self in self._window.pressed_operations: + for button in self._window.pressed_operations: + if button is self: + continue + + button.move(button.mapToParent(event.pos() - self._mouse_press_pos)) self._window.update() super(DragButton, self).mouseMoveEvent(event) def mouseReleaseEvent(self, event): - if self._mouse_press_pos is not None: - moved = event.pos() - self._mouse_press_pos - if moved.manhattanLength() > 3: - event.ignore() - return + self._m_press = False + if self._m_drag: + if self._mouse_press_pos is not None: + moved = event.pos() - self._mouse_press_pos + if moved.manhattanLength() > 3: + event.ignore() + + self._m_drag = False + + else: + self.select_button(event.modifiers()) super(DragButton, self).mouseReleaseEvent(event) + def _toggle_button(self, pressed=False): + self.pressed = not pressed + self.setStyleSheet(f"background-color: {'white' if not self.pressed else 'grey'}; border-style: solid;\ + border-color: black; border-width: 2px") + path_to_image = os.path.join('operation_icons', f"{self.operation_path_name}{'_grey.png' if self.pressed else '.png'}") + self.setIcon(QIcon(path_to_image)) + self.setIconSize(QSize(55, 55)) + + def select_button(self, modifiers=None): + if modifiers != Qt.ControlModifier: + for button in self._window.pressed_operations: + button._toggle_button(button.pressed) + + self._toggle_button(self.pressed) + self._window.pressed_operations = [self] + + else: + self._toggle_button(self.pressed) + if self in self._window.pressed_operations: + self._window.pressed_operations.remove(self) + else: + self._window.pressed_operations.append(self) + + for signal in self._window.signalList: + signal.update() + def remove(self): - self.deleteLater() \ No newline at end of file + self._window.scene.removeItem(self._window.operationDict[self]) + + _signals = [] + for signal, ports in self._window.signalPortDict.items(): + if any([port in self._window.portDict[self] for port in ports]): + signal.remove() + _signals.append(signal) + + for signal in _signals: + del self._window.signalPortDict[signal] + + for port in self._window.portDict[self]: + if port in self._window.pressed_ports: + self._window.pressed_ports.remove(port) + + if self in self._window.pressed_operations: + self._window.pressed_operations.remove(self) + + if self in self._window.operationDict.keys(): + del self._window.operationDict[self] \ No newline at end of file diff --git a/b_asic/GUI/gui_interface.py b/b_asic/GUI/gui_interface.py index 2d42d808f24d6c55850ae122979f1498f2191a2f..d026fd399a3c7a9281c75f0f9e62a19f7bbf4aaa 100644 --- a/b_asic/GUI/gui_interface.py +++ b/b_asic/GUI/gui_interface.py @@ -223,6 +223,8 @@ class Ui_main_window(object): self.edit_menu.setObjectName("edit_menu") self.view_menu = QtWidgets.QMenu(self.menu_bar) self.view_menu.setObjectName("view_menu") + self.run_menu = QtWidgets.QMenu(self.menu_bar) + self.run_menu.setObjectName("run_menu") main_window.setMenuBar(self.menu_bar) self.status_bar = QtWidgets.QStatusBar(main_window) self.status_bar.setObjectName("status_bar") @@ -235,6 +237,8 @@ class Ui_main_window(object): self.actionUndo.setObjectName("actionUndo") self.actionRedo = QtWidgets.QAction(main_window) self.actionRedo.setObjectName("actionRedo") + self.actionSimulateSFG = QtWidgets.QAction(main_window) + self.actionSimulateSFG.setObjectName("actionSimulateSFG") self.actionToolbar = QtWidgets.QAction(main_window) self.actionToolbar.setCheckable(True) self.actionToolbar.setObjectName("actionToolbar") @@ -244,9 +248,11 @@ class Ui_main_window(object): self.edit_menu.addAction(self.actionUndo) self.edit_menu.addAction(self.actionRedo) self.view_menu.addAction(self.actionToolbar) + self.run_menu.addAction(self.actionSimulateSFG) self.menu_bar.addAction(self.file_menu.menuAction()) self.menu_bar.addAction(self.edit_menu.menuAction()) self.menu_bar.addAction(self.view_menu.menuAction()) + self.menu_bar.addAction(self.run_menu.menuAction()) self.retranslateUi(main_window) self.operation_list.setCurrentIndex(1) @@ -269,6 +275,8 @@ class Ui_main_window(object): self.file_menu.setTitle(_translate("main_window", "File")) self.edit_menu.setTitle(_translate("main_window", "Edit")) self.view_menu.setTitle(_translate("main_window", "View")) + self.run_menu.setTitle(_translate("main_window", "Run")) + self.actionSimulateSFG.setText(_translate("main_window", "Simulate SFG")) self.save_menu.setText(_translate("main_window", "Save")) self.exit_menu.setText(_translate("main_window", "Exit")) self.exit_menu.setShortcut(_translate("main_window", "Ctrl+Q")) diff --git a/b_asic/GUI/main_window.py b/b_asic/GUI/main_window.py index ffa8448a7b2ed93b2b7cb79d3c609ac2fed34303..71cb5cb99a2bf410d7a818547230f24004cf090c 100644 --- a/b_asic/GUI/main_window.py +++ b/b_asic/GUI/main_window.py @@ -3,6 +3,7 @@ B-ASIC GUI Module. This python file is the main window of the GUI for B-ASIC. """ +from pprint import pprint from os import getcwd, path import sys @@ -11,12 +12,12 @@ from gui_interface import Ui_main_window from arrow import Arrow from port_button import PortButton -from b_asic import Operation +from b_asic import Operation, SFG, InputPort, OutputPort +from b_asic.simulation import Simulation import b_asic.core_operations as c_oper import b_asic.special_operations as s_oper from utils import decorate_class, handle_error -from b_asic import SFG -from b_asic import InputPort, OutputPort +from simulate_sfg_window import SimulateSFGWindow, Plot from numpy import linspace @@ -24,11 +25,10 @@ from PySide2.QtWidgets import QApplication, QWidget, QMainWindow, QLabel, QActio QStatusBar, QMenuBar, QLineEdit, QPushButton, QSlider, QScrollArea, QVBoxLayout,\ QHBoxLayout, QDockWidget, QToolBar, QMenu, QLayout, QSizePolicy, QListWidget,\ QListWidgetItem, QGraphicsView, QGraphicsScene, QShortcut, QGraphicsTextItem,\ -QGraphicsProxyWidget +QGraphicsProxyWidget, QInputDialog from PySide2.QtCore import Qt, QSize from PySide2.QtGui import QIcon, QFont, QPainter, QPen, QBrush, QKeySequence - MIN_WIDTH_SCENE = 600 MIN_HEIGHT_SCENE = 520 @@ -44,21 +44,25 @@ class MainWindow(QMainWindow): self._operations_from_name = dict() self.zoom = 1 self.sfg_name_i = 0 - self.operationList = [] + self.operationDict = dict() + self.operationItemSceneList = [] self.signalList = [] self.pressed_operations = [] - self.portList = [] + self.portDict = dict() + self.signalPortDict = dict() self.pressed_ports = [] self.sfg_list = [] self.source = None self._window = self - self.init_ui() self.add_operations_from_namespace(c_oper, self.ui.core_operations_list) self.add_operations_from_namespace(s_oper, self.ui.special_operations_list) self.shortcut_core = QShortcut(QKeySequence("Ctrl+R"), self.ui.operation_box) self.shortcut_core.activated.connect(self._refresh_operations_list_from_namespace) + self.shortcut_select_all = QShortcut(QKeySequence("Ctrl+A"), self.graphic_view) + self.shortcut_select_all.activated.connect(self._select_all_operations) + self.scene.selectionChanged.connect(self._select_operations) self.move_button_index = 0 self.is_show_names = True @@ -69,6 +73,8 @@ class MainWindow(QMainWindow): self.check_show_names.setChecked(1) self.ui.view_menu.addAction(self.check_show_names) + self.ui.actionSimulateSFG.triggered.connect(self.simulate_sfg) + def init_ui(self): self.ui.core_operations_list.itemClicked.connect(self.on_list_widget_item_clicked) self.ui.special_operations_list.itemClicked.connect(self.on_list_widget_item_clicked) @@ -81,7 +87,7 @@ class MainWindow(QMainWindow): self.graphic_view = QGraphicsView(self.scene, self) self.graphic_view.setRenderHint(QPainter.Antialiasing) self.graphic_view.setGeometry(self.ui.operation_box.width(), 0, self.width(), self.height()) - self.graphic_view.setDragMode(QGraphicsView.ScrollHandDrag) + self.graphic_view.setDragMode(QGraphicsView.RubberBandDrag) def create_toolbar_view(self): self.toolbar = self.addToolBar("Toolbar") @@ -99,16 +105,16 @@ class MainWindow(QMainWindow): self.graphic_view.scale(self.zoom, self.zoom) self.zoom = old_zoom - def view_operation_names(self, event): + def view_operation_names(self): if self.check_show_names.isChecked(): self.is_show_names = True else: self.is_show_names = False - for operation in self.operationList: + for operation in self.operationDict.keys(): operation.label.setOpacity(self.is_show_names) operation.is_show_name = self.is_show_names - def exit_app(self, checked): + def exit_app(self): QApplication.quit() def create_SFG_from_toolbar(self): @@ -120,8 +126,9 @@ class MainWindow(QMainWindow): elif isinstance(op.operation, s_oper.Output): outputs.append(op.operation) - self.sfg_name_i += 1 - sfg = SFG(inputs=inputs, outputs=outputs, name="sfg" + str(self.sfg_name_i)) + name = QInputDialog.getText(self, "Create SFG", "Name: ", QLineEdit.Normal) + + sfg = SFG(inputs=inputs, outputs=outputs, name=name[0]) for op in self.pressed_operations: op.setToolTip(sfg.name) self.sfg_list.append(sfg) @@ -132,24 +139,22 @@ class MainWindow(QMainWindow): """ return [length / 2] if ports == 1 else linspace(0, length, ports) - def _create_port(self, operation, port, output_port=True): - text = ">" if output_port else "<" - button = PortButton(text, operation, port, self) - button.setStyleSheet("background-color: white") - button.connectionRequested.connect(self.connectButton) - return button - def add_ports(self, operation): _output_ports_dist = self._determine_port_distance(55 - 17, operation.operation.output_count) _input_ports_dist = self._determine_port_distance(55 - 17, operation.operation.input_count) + self.portDict[operation] = list() for i, dist in enumerate(_input_ports_dist): - port = self._create_port(operation, operation.operation.input(i)) + port = PortButton(">", operation, operation.operation.input(i), self) + self.portDict[operation].append(port) + operation.ports.append(port) port.move(0, dist) port.show() for i, dist in enumerate(_output_ports_dist): - port = self._create_port(operation, operation.operation.output(i)) + port = PortButton(">", operation, operation.operation.output(i), self) + self.portDict[operation].append(port) + operation.ports.append(port) port.move(55 - 12, dist) port.show() @@ -188,6 +193,7 @@ class MainWindow(QMainWindow): attr_button.setParent(None) attr_button_scene = self.scene.addWidget(attr_button) attr_button_scene.moveBy(self.move_button_index * 100, 0) + attr_button_scene.setFlag(attr_button_scene.ItemIsSelectable, True) self.move_button_index += 1 operation_label = QGraphicsTextItem(attr_oper.type_name(), attr_button_scene) if not self.is_show_names: @@ -195,7 +201,7 @@ class MainWindow(QMainWindow): operation_label.setTransformOriginPoint(operation_label.boundingRect().center()) operation_label.moveBy(10, -20) attr_button.add_label(operation_label) - self.operationList.append(attr_button) + self.operationDict[attr_button] = attr_button_scene except Exception as e: print("Unexpected error occured: ", e) @@ -210,13 +216,8 @@ class MainWindow(QMainWindow): self._create_operation(item) def keyPressEvent(self, event): - pressed_operations = [] - for op in self.operationList: - if op.pressed: - pressed_operations.append(op) if event.key() == Qt.Key_Delete: - for pressed_op in pressed_operations: - self.operationList.remove(pressed_op) + for pressed_op in self.pressed_operations: pressed_op.remove() self.move_button_index -= 1 super().keyPressEvent(event) @@ -228,15 +229,62 @@ class MainWindow(QMainWindow): if isinstance(self.pressed_ports[i].port, OutputPort) and \ isinstance(self.pressed_ports[i+1].port, InputPort): line = Arrow(self.pressed_ports[i], self.pressed_ports[i + 1], self) + self.signalPortDict[line] = [self.pressed_ports[i], self.pressed_ports[i + 1]] + self.scene.addItem(line) self.signalList.append(line) + for port in self.pressed_ports: + port.select_port() + self.update() def paintEvent(self, event): - for signal in self.signalList: + for signal in self.signalPortDict.keys(): signal.moveLine() + def _select_all_operations(self): + self.pressed_operations.clear() + for button in self.operationDict.keys(): + button._toggle_button(pressed=False) + self.pressed_operations.append(button) + + def _select_operations(self): + selected = [button.widget() for button in self.scene.selectedItems()] + for button in selected: + button._toggle_button(pressed=False) + + for button in self.pressed_operations: + if button not in selected: + button._toggle_button(pressed=True) + + self.pressed_operations = selected + + def _simulate_sfg(self): + for sfg, properties in self.dialog.properties.items(): + simulation = Simulation(sfg, input_providers=properties["input_values"], save_results=properties["all_results"]) + l_result = simulation.run_for(properties["iteration_count"]) + + if properties["all_results"]: + print(f"{'=' * 10} {sfg.name} {'=' * 10}") + pprint(simulation.results) + print(f"{'=' * 10} /{sfg.name} {'=' * 10}") + + if properties["show_plot"]: + self.plot = Plot(simulation, sfg) + self.plot.show() + + def simulate_sfg(self): + self.dialog = SimulateSFGWindow(self) + + for sfg in self.sfg_list: + self.dialog.add_sfg_to_dialog(sfg) + + self.dialog.show() + + # Wait for input to dialog. Kinda buggy because of the separate window in the same thread. + self.dialog.simulate.connect(self._simulate_sfg) + if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() diff --git a/b_asic/GUI/port_button.py b/b_asic/GUI/port_button.py index 04b355663cd057b399c6e2c182f70aebeddffc50..11c36ef5206ae4935a5bcf675a10eee22239d8dc 100644 --- a/b_asic/GUI/port_button.py +++ b/b_asic/GUI/port_button.py @@ -8,12 +8,17 @@ class PortButton(QPushButton): connectionRequested = Signal(QPushButton) moved = Signal() def __init__(self, name, operation, port, window, parent=None): + super(PortButton, self).__init__(name, operation, parent) self.pressed = False - self.window = window + self._window = window self.port = port self.operation = operation self.clicked = 0 - super(PortButton, self).__init__(name, operation) + self._m_drag = False + self._m_press = False + + self.setStyleSheet("background-color: white") + self.connectionRequested.connect(self._window.connectButton) def contextMenuEvent(self, event): menu = QMenu() @@ -22,18 +27,32 @@ class PortButton(QPushButton): def mousePressEvent(self, event): if event.button() == Qt.LeftButton: - self.clicked += 1 - if self.clicked == 1: - self.setStyleSheet("background-color: grey") - self.pressed = True - self.window.pressed_ports.append(self) - elif self.clicked == 2: - self.setStyleSheet("background-color: white") - self.pressed = False - self.clicked = 0 - self.window.pressed_ports.remove(self) + self.select_port(event.modifiers()) + super(PortButton, self).mousePressEvent(event) def mouseReleaseEvent(self, event): super(PortButton, self).mouseReleaseEvent(event) + def _toggle_port(self, pressed=False): + self.pressed = not pressed + self.setStyleSheet(f"background-color: {'white' if not self.pressed else 'grey'}") + + def select_port(self, modifiers=None): + if modifiers != Qt.ControlModifier: + for port in self._window.pressed_ports: + port._toggle_port(port.pressed) + + self._toggle_port(self.pressed) + self._window.pressed_ports = [self] + + else: + self._toggle_port(self.pressed) + if self in self._window.pressed_ports: + self._window.pressed_ports.remove(self) + else: + self._window.pressed_ports.append(self) + + for signal in self._window.signalList: + signal.update() + diff --git a/b_asic/GUI/simulate_sfg_window.py b/b_asic/GUI/simulate_sfg_window.py new file mode 100644 index 0000000000000000000000000000000000000000..9976adf0ed554b7590785543473b19f5c8b49ef4 --- /dev/null +++ b/b_asic/GUI/simulate_sfg_window.py @@ -0,0 +1,118 @@ +from PySide2.QtWidgets import QDialog, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout,\ +QLabel, QCheckBox, QSpinBox, QGroupBox, QFrame, QFormLayout, QGridLayout, QSizePolicy +from PySide2.QtCore import Qt, Signal +from PySide2.QtGui import QIntValidator + +from matplotlib.backends import qt_compat +from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas +from matplotlib.figure import Figure + + +class SimulateSFGWindow(QDialog): + simulate = Signal() + + def __init__(self, window): + super(SimulateSFGWindow, self).__init__() + self._window = window + self.properties = dict() + self.sfg_to_layout = dict() + self.setWindowFlags(Qt.WindowTitleHint | Qt.WindowCloseButtonHint) + self.setWindowTitle("Simulate SFG") + + self.dialog_layout = QVBoxLayout() + self.simulate_btn = QPushButton("Simulate") + self.simulate_btn.clicked.connect(self.save_properties) + self.dialog_layout.addWidget(self.simulate_btn) + self.setLayout(self.dialog_layout) + + def add_sfg_to_dialog(self, sfg): + sfg_layout = QVBoxLayout() + options_layout = QFormLayout() + + name_label = QLabel(f"{sfg.name}") + sfg_layout.addWidget(name_label) + + spin_box = QSpinBox() + spin_box.setRange(0, 2147483647) + options_layout.addRow("Iteration Count: ", spin_box) + + check_box = QCheckBox() + options_layout.addRow("Plot Results: ", check_box) + + check_box = QCheckBox() + options_layout.addRow("Get All Results: ", check_box) + + input_layout = QVBoxLayout() + input_label = QLabel("Input Values: ") + input_layout.addWidget(input_label) + input_grid = QGridLayout() + x, y = 0, 0 + for i in range(sfg.input_count): + if i % 2 == 0 and i > 0: + x += 1 + y = 0 + + _input_value = QLineEdit() + _input_value.setValidator(QIntValidator()) + _input_value.setFixedWidth(50) + input_grid.addWidget(_input_value, x, y) + y += 1 + + input_layout.addLayout(input_grid) + sfg_layout.addLayout(options_layout) + sfg_layout.addLayout(input_layout) + + frame = QFrame() + frame.setFrameShape(QFrame.HLine) + frame.setFrameShadow(QFrame.Sunken) + self.dialog_layout.addWidget(frame) + + self.sfg_to_layout[sfg] = sfg_layout + self.dialog_layout.addLayout(sfg_layout) + + def save_properties(self): + for sfg, sfg_row in self.sfg_to_layout.items(): + _option_values = sfg_row.children()[0] + _input_values = sfg_row.children()[1].children()[0] + _, spin_box, _, check_box_plot, _, check_box_all = map(lambda x: _option_values.itemAt(x).widget(), range(_option_values.count())) + input_values = map(lambda x: _input_values.itemAt(x).widget(), range(_input_values.count())) + input_values = [int(widget.text()) if widget.text() else 0 for widget in input_values] + self.properties[sfg] = { + "iteration_count": spin_box.value(), + "show_plot": check_box_plot.isChecked(), + "all_results": check_box_all.isChecked(), + "input_values": input_values + } + + # If we plot we should also print the entire data, since you can't really interact with the graph. + if check_box_plot.isChecked(): + self.properties[sfg]["all_results"] = True + + self.accept() + self.simulate.emit() + + +class Plot(FigureCanvas): + def __init__(self, simulation, sfg, parent=None, width=5, height=4, dpi=100): + self.simulation = simulation + self.sfg = sfg + + fig = Figure(figsize=(width, height), dpi=dpi) + fig.suptitle(sfg.name, fontsize=20) + self.axes = fig.add_subplot(111) + + FigureCanvas.__init__(self, fig) + self.setParent(parent) + + FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding) + FigureCanvas.updateGeometry(self) + self._plot_values_sfg() + + def _plot_values_sfg(self): + x_axis = list(range(len(self.simulation.results.keys()))) + for _output in range(self.sfg.output_count): + y_axis = list() + for _iter in range(len(self.simulation.results.keys())): + y_axis.append(self.simulation.results[_iter][str(_output)]) + + self.axes.plot(x_axis, y_axis) diff --git a/b_asic/core_operations.py b/b_asic/core_operations.py index ec7306c6f4c97b5c0377794e48524d09c7ed159b..a70c86b7d966197850604ad7a269242d364eca36 100644 --- a/b_asic/core_operations.py +++ b/b_asic/core_operations.py @@ -249,8 +249,8 @@ class MAD(AbstractOperation): def __init__(self, src0: Optional[SignalSourceProvider] = None, src1: Optional[SignalSourceProvider] = None, src2: Optional[SignalSourceProvider] = None, name: Name = ""): super().__init__(input_count = 3, output_count = 1, name = name, input_sources = [src0, src1, src2]) - @property - def type_name(self) -> TypeName: + @classmethod + def type_name(cls) -> TypeName: return "mad" def evaluate(self, a, b, c): diff --git a/setup.py b/setup.py index 94285a70c496dc08923a9a36258316e8397670c6..f91d34d54336708275a28b1f9f02ea1b2d80141f 100644 --- a/setup.py +++ b/setup.py @@ -72,10 +72,11 @@ setuptools.setup( "pybind11>=2.3.0", "numpy", "pyside2", - "graphviz" + "graphviz", + "matplotlib" ], packages = ["b_asic"], ext_modules = [CMakeExtension("b_asic")], cmdclass = {"build_ext": CMakeBuild}, zip_safe = False -) \ No newline at end of file +)