diff --git a/GUI/main_window.py b/GUI/main_window.py new file mode 100644 index 0000000000000000000000000000000000000000..53dacce61b5acbfe9de9607a2499c57e0a386eae --- /dev/null +++ b/GUI/main_window.py @@ -0,0 +1,200 @@ +"""@package docstring +B-ASIC GUI Module. +This python file is an example of how a GUI can be implemented +using buttons and textboxes. +""" + +import sys + +from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QLabel, QAction,\ +QStatusBar, QMenuBar, QLineEdit, QPushButton +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QIcon, QFont, QPainter, QPen + + +class DragButton(QPushButton): + """How to create a dragbutton""" + def mousePressEvent(self, event): + self._mouse_press_pos = None + self._mouse_move_pos = None + if event.button() == Qt.LeftButton: + self._mouse_press_pos = event.globalPos() + self._mouse_move_pos = event.globalPos() + + super(DragButton, self).mousePressEvent(event) + + def mouseMoveEvent(self, event): + if event.buttons() == Qt.LeftButton: + cur_pos = self.mapToGlobal(self.pos()) + global_pos = event.globalPos() + diff = global_pos - self._mouse_move_pos + new_pos = self.mapFromGlobal(cur_pos + diff) + self.move(new_pos) + + self._mouse_move_pos = global_pos + + super(DragButton, self).mouseMoveEvent(event) + + def mouseReleaseEvent(self, event): + if self._mouse_press_pos is not None: + moved = event.globalPos() - self._mouse_press_pos + if moved.manhattanLength() > 3: + event.ignore() + return + + super(DragButton, self).mouseReleaseEvent(event) + +class SubWindow(QWidget): + """Creates a sub window """ + def create_window(self, window_width, window_height): + """Creates a window + """ + parent = None + super(SubWindow, self).__init__(parent) + self.setWindowFlags(Qt.WindowStaysOnTopHint) + self.resize(window_width, window_height) + +class MainWindow(QMainWindow): + """Main window for the program""" + def __init__(self, *args, **kwargs): + super(MainWindow, self).__init__(*args, **kwargs) + + self.setWindowTitle(" ") + self.setWindowIcon(QIcon('small_logo.png')) + + # Menu buttons + test_button = QAction("Test", self) + + exit_button = QAction("Exit", self) + exit_button.setShortcut("Ctrl+Q") + exit_button.triggered.connect(self.exit_app) + + edit_button = QAction("Edit", self) + edit_button.setStatusTip("Open edit menu") + edit_button.triggered.connect(self.on_edit_button_click) + + view_button = QAction("View", self) + view_button.setStatusTip("Open view menu") + view_button.triggered.connect(self.on_view_button_click) + + menu_bar = QMenuBar() + menu_bar.setStyleSheet("background-color:rgb(222, 222, 222)") + self.setMenuBar(menu_bar) + + file_menu = menu_bar.addMenu("&File") + file_menu.addAction(exit_button) + file_menu.addSeparator() + file_menu.addAction(test_button) + + edit_menu = menu_bar.addMenu("&Edit") + edit_menu.addAction(edit_button) + + edit_menu.addSeparator() + + view_menu = menu_bar.addMenu("&View") + view_menu.addAction(view_button) + + self.setStatusBar(QStatusBar(self)) + + def on_file_button_click(self): + print("File") + + def on_edit_button_click(self): + print("Edit") + + def on_view_button_click(self): + print("View") + + def exit_app(self, checked): + QApplication.quit() + + def clicked(self): + print("Drag button clicked") + + def add_drag_buttons(self): + """Adds draggable buttons""" + addition_button = DragButton("Addition", self) + addition_button.move(10, 130) + addition_button.setFixedSize(70, 20) + addition_button.clicked.connect(self.create_sub_window) + + addition_button2 = DragButton("Addition", self) + addition_button2.move(10, 130) + addition_button2.setFixedSize(70, 20) + addition_button2.clicked.connect(self.create_sub_window) + + subtraction_button = DragButton("Subtraction", self) + subtraction_button.move(10, 170) + subtraction_button.setFixedSize(70, 20) + subtraction_button.clicked.connect(self.create_sub_window) + + subtraction_button2 = DragButton("Subtraction", self) + subtraction_button2.move(10, 170) + subtraction_button2.setFixedSize(70, 20) + subtraction_button2.clicked.connect(self.create_sub_window) + + multiplication_button = DragButton("Multiplication", self) + multiplication_button.move(10, 210) + multiplication_button.setFixedSize(70, 20) + multiplication_button.clicked.connect(self.create_sub_window) + + multiplication_button2 = DragButton("Multiplication", self) + multiplication_button2.move(10, 210) + multiplication_button2.setFixedSize(70, 20) + multiplication_button2.clicked.connect(self.create_sub_window) + + def paintEvent(self, e): + # Temporary black box for operations + painter = QPainter(self) + painter.setPen(QPen(Qt.black, 5, Qt.SolidLine)) + painter.drawRect(0, 110, 100, 400) + + # Temporary arrow resembling a signal + painter.setRenderHint(QPainter.Antialiasing) + painter.setPen(Qt.black) + painter.setBrush(Qt.white) + painter.drawLine(300, 200, 400, 200) + painter.drawLine(400, 200, 395, 195) + painter.drawLine(400, 200, 395, 205) + + def create_sub_window(self): + """ Example of how to create a sub window + """ + self.sub_window = SubWindow() + self.sub_window.create_window(400, 300) + self.sub_window.setWindowTitle("Properties") + + self.sub_window.properties_label = QLabel(self.sub_window) + self.sub_window.properties_label.setText('Properties') + self.sub_window.properties_label.setFixedWidth(400) + self.sub_window.properties_label.setFont(QFont('SansSerif', 14, QFont.Bold)) + self.sub_window.properties_label.setAlignment(Qt.AlignCenter) + + self.sub_window.name_label = QLabel(self.sub_window) + self.sub_window.name_label.setText('Name:') + self.sub_window.name_label.move(20, 40) + + self.sub_window.name_line = QLineEdit(self.sub_window) + self.sub_window.name_line.setPlaceholderText("Write a name here") + self.sub_window.name_line.move(70, 40) + self.sub_window.name_line.resize(100, 20) + + self.sub_window.id_label = QLabel(self.sub_window) + self.sub_window.id_label.setText('Id:') + self.sub_window.id_label.move(20, 70) + + self.sub_window.id_line = QLineEdit(self.sub_window) + self.sub_window.id_line.setPlaceholderText("Write an id here") + self.sub_window.id_line.move(70, 70) + self.sub_window.id_line.resize(100, 20) + + self.sub_window.show() + + +if __name__ == "__main__": + app = QApplication(sys.argv) + window = MainWindow() + window.add_drag_buttons() + window.resize(960, 720) + window.show() + app.exec_() diff --git a/b_asic/operation.py b/b_asic/operation.py index c75ca87b96d8794a4f3eb6ee939c446d3f02b2df..a0d0f48a1f7429ce0d393ad4e93ef24c84914f7b 100644 --- a/b_asic/operation.py +++ b/b_asic/operation.py @@ -348,7 +348,8 @@ class AbstractOperation(Operation, AbstractGraphComponent): def source(self) -> OutputPort: if self.output_count != 1: diff = "more" if self.output_count > 1 else "less" - raise TypeError(f"{self.__class__.__name__} cannot be used as an input source because it has {diff} than 1 output") + raise TypeError( + f"{self.__class__.__name__} cannot be used as an input source because it has {diff} than 1 output") return self.output(0) def truncate_input(self, index: int, value: Number, bits: int) -> Number: @@ -372,4 +373,4 @@ class AbstractOperation(Operation, AbstractGraphComponent): args.append(self.truncate_input(i, input_values[i], bits)) else: args.append(input_values[i]) - return args \ No newline at end of file + return args diff --git a/b_asic/signal_flow_graph.py b/b_asic/signal_flow_graph.py index 573fb21c3e0c80a72de06af3621d87bcef7c0d5d..950926f48212e825125a0122a272def557499b69 100644 --- a/b_asic/signal_flow_graph.py +++ b/b_asic/signal_flow_graph.py @@ -155,6 +155,49 @@ class SFG(AbstractOperation): raise ValueError(f"Output signal #{output_index} is missing source in SFG") if signal.source.operation not in self._original_components_to_new: self._add_operation_connected_tree_copy(signal.source.operation) + + def __str__(self): + """Get a string representation of this SFG.""" + output_string = "" + for component in self._components_ordered: + if isinstance(component, Operation): + for key, value in self._components_by_id.items(): + if value is component: + output_string += "id: " + key + ", name: " + + if component.name != None: + output_string += component.name + ", " + else: + output_string += "-, " + + if component.type_name is "c": + output_string += "value: " + str(component.value) + ", input: [" + else: + output_string += "input: [" + + counter_input = 0 + for input in component.inputs: + counter_input += 1 + for signal in input.signals: + for key, value in self._components_by_id.items(): + if value is signal: + output_string += key + ", " + + if counter_input > 0: + output_string = output_string[:-2] + output_string += "], output: [" + counter_output = 0 + for output in component.outputs: + counter_output += 1 + for signal in output.signals: + for key, value in self._components_by_id.items(): + if value is signal: + output_string += key + ", " + if counter_output > 0: + output_string = output_string[:-2] + output_string += "]\n" + + return output_string def __call__(self, *src: Optional[SignalSourceProvider], name: Name = "") -> "SFG": """Get a new independent SFG instance that is identical to this SFG except without any of its external connections.""" diff --git a/small_logo.png b/small_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..689a38192b9fc4c6ed490e1e0001fd5dd264c968 Binary files /dev/null and b/small_logo.png differ diff --git a/test/test_print_sfg.py b/test/test_print_sfg.py new file mode 100644 index 0000000000000000000000000000000000000000..49b0950d82857f86ba652e76075b5d3cb40e1584 --- /dev/null +++ b/test/test_print_sfg.py @@ -0,0 +1,46 @@ +""" +B-ASIC test suite for printing a SFG +""" + + +from b_asic.signal_flow_graph import SFG +from b_asic.core_operations import Addition, Multiplication, Constant +from b_asic.port import InputPort, OutputPort +from b_asic.signal import Signal +from b_asic.special_operations import Input, Output + +import pytest + + +class TestPrintSfg: + def test_print_one_addition(self): + inp1 = Input("INP1") + inp2 = Input("INP2") + add1 = Addition(inp1, inp2, "ADD1") + out1 = Output(add1, "OUT1") + sfg = SFG(inputs=[inp1, inp2], outputs=[out1], name="sf1") + + assert sfg.__str__() == ("id: add1, name: ADD1, input: [s1, s2], output: [s3]\nid: in1, name: INP1, input: [], output: [s1]\nid: in2, name: INP2, input: [], output: [s2]\nid: out1, name: OUT1, input: [s3], output: []\n") + + def test_print_add_mul(self): + inp1 = Input("INP1") + inp2 = Input("INP2") + inp3 = Input("INP3") + add1 = Addition(inp1, inp2, "ADD1") + mul1 = Multiplication(add1, inp3, "MUL1") + out1 = Output(mul1, "OUT1") + sfg = SFG(inputs=[inp1, inp2, inp3], outputs=[out1], name="mac_sfg") + + assert sfg.__str__() == ("id: add1, name: ADD1, input: [s1, s2], output: [s5]\nid: in1, name: INP1, input: [], output: [s1]\nid: in2, name: INP2, input: [], output: [s2]\nid: mul1, name: MUL1, input: [s5, s3], output: [s4]\nid: in3, name: INP3, input: [], output: [s3]\nid: out1, name: OUT1, input: [s4], output: []\n") + + def test_print_constant(self): + inp1 = Input("INP1") + const1 = Constant(3, "CONST") + add1 = Addition(const1, inp1, "ADD1") + out1 = Output(add1, "OUT1") + + sfg = SFG(inputs=[inp1], outputs=[out1], name="sfg") + + assert sfg.__str__() == ("id: add1, name: ADD1, input: [s3, s1], output: [s2]\nid: c1, name: CONST, value: 3, input: [], output: [s3]\nid: in1, name: INP1, input: [], output: [s1]\nid: out1, name: OUT1, input: [s2], output: []\n") + + \ No newline at end of file