Skip to content
Snippets Groups Projects
Commit e452134c authored by Jacob Wahlman's avatar Jacob Wahlman :ok_hand:
Browse files

added logging and help section

parent 9dc600c2
No related branches found
No related tags found
2 merge requests!67WIP: B-ASIC version 1.0.0 hotfix,!65B-ASIC version 1.0.0
from PySide2.QtWidgets import QVBoxLayout, QHBoxLayout, QWidget, QDialog, QLabel, QFrame, QScrollArea
from PySide2.QtCore import Qt
QUESTIONS = {
"Adding operations": "Select an operation under 'Special operations' or 'Core operations' to add it to the workspace.",
"Moving operations": "To drag an operation, select the operation on the workspace and drag it around.",
"Selecting operations": "To select one operation just press it once, it will then turn grey.",
"Selecting multiple operations using dragging": "To select multiple operations using your mouse, \ndrag the mouse while pressing left mouse button, any operation under the selection box will then be selected.",
"Selecting multiple operations using without dragging": "To select mutliple operations using without dragging, \npress 'Ctrl+LMouseButton' on any operation. Alternatively press 'Ctrl+A' to select all operations.",
"Remove operations": "To remove an operation, select the operation to be deleted, \nfinally press RMouseButton to bring up the context menu, then press 'Delete'.",
"Remove multiple operations": "To remove multiple operations, \nselect all operations to be deleted and press 'Delete' on your keyboard.",
"Connecting operations": "To connect operations, select the ports on the operation to connect from, \nthen select the next port by pressing 'Ctrl+LMouseButton' on the destination port. Tip: You can chain connection by selecting the ports in the order they should be connected.",
"Creating a signal-flow-graph": "To create a signal-flow-graph (SFG), \ncouple together the operations you wish to create a sfg from, then select all operations you wish to include in the sfg, \nfinally press 'Create SFG' in the upper left corner and enter the name of the sfg.",
"Simulating a signal-flow-graph": "To simulate a signal-flow-graph (SFG), press the run button in the toolbar, \nthen press 'Simulate SFG' and enter the properties of the simulation.",
"Properties of simulation": "The properties of the simulation are, 'Iteration Count': The number of iterations to run the simulation for, \n'Plot Results': Open a plot over the output in matplotlib, \n'Get All Results': Print the detailed output from simulating the sfg in the terminal, \n'Input Values': The input values to the SFG by index of the port."
}
class KeybindsWindow(QDialog):
def __init__(self, window):
super(KeybindsWindow, self).__init__()
self._window = window
self.setWindowFlags(Qt.WindowTitleHint | Qt.WindowCloseButtonHint)
self.setWindowTitle("B-ASIC Keybinds")
self.dialog_layout = QVBoxLayout()
self.setLayout(self.dialog_layout)
self.add_information_to_layout()
def add_information_to_layout(self):
information_layout = QVBoxLayout()
title_label = QLabel("B-ASIC / Better ASIC Toolbox")
subtitle_label = QLabel("Keybinds in the GUI.")
frame = QFrame()
frame.setFrameShape(QFrame.HLine)
frame.setFrameShadow(QFrame.Sunken)
self.dialog_layout.addWidget(frame)
keybinds_label = QLabel(
"'Ctrl+A' - Select all operations on the workspace.\n"
"'Ctrl+R' - Reload the operation list to add any new operations created.\n"
"'Ctrl+Q' - Quit the application.\n"
"'Ctrl+LMouseButton' - On a operation will select the operation, without deselecting the other operations.\n"
"'Ctrl+S' (Plot) - Save the plot if a plot is visible.\n"
"'Ctrl+?' - Open the FAQ section."
)
information_layout.addWidget(title_label)
information_layout.addWidget(subtitle_label)
self.dialog_layout.addLayout(information_layout)
self.dialog_layout.addWidget(frame)
self.dialog_layout.addWidget(keybinds_label)
class AboutWindow(QDialog):
def __init__(self, window):
super(AboutWindow, self).__init__()
self._window = window
self.setWindowFlags(Qt.WindowTitleHint | Qt.WindowCloseButtonHint)
self.setWindowTitle("About B-ASIC")
self.dialog_layout = QVBoxLayout()
self.setLayout(self.dialog_layout)
self.add_information_to_layout()
def add_information_to_layout(self):
information_layout = QVBoxLayout()
title_label = QLabel("B-ASIC / Better ASIC Toolbox")
subtitle_label = QLabel("Construct, simulate and analyze components of an ASIC.")
frame = QFrame()
frame.setFrameShape(QFrame.HLine)
frame.setFrameShadow(QFrame.Sunken)
self.dialog_layout.addWidget(frame)
about_label = QLabel(
"B-ASIC is a open source tool using the B-ASIC library to construct, simulate and analyze ASICs.\n"
"B-ASIC is developed under the MIT-license and any extension to the program should follow that same license.\n"
"To read more about how the GUI works please refer to the FAQ under 'Help'."
)
information_layout.addWidget(title_label)
information_layout.addWidget(subtitle_label)
self.dialog_layout.addLayout(information_layout)
self.dialog_layout.addWidget(frame)
self.dialog_layout.addWidget(about_label)
class FaqWindow(QDialog):
def __init__(self, window):
super(FaqWindow, self).__init__()
self._window = window
self.setWindowFlags(Qt.WindowTitleHint | Qt.WindowCloseButtonHint)
self.setWindowTitle("Frequently Asked Questions")
self.dialog_layout = QVBoxLayout()
self.scroll_area = QScrollArea()
self.setLayout(self.dialog_layout)
for question, answer in QUESTIONS.items():
self.add_question_to_layout(question, answer)
self.scroll_area.setWidget(self)
self.scroll_area.setWidgetResizable(True)
def add_question_to_layout(self, question, answer):
question_layout = QVBoxLayout()
answer_layout = QHBoxLayout()
question_label = QLabel(question)
question_layout.addWidget(question_label)
answer_label = QLabel(answer)
answer_layout.addWidget(answer_label)
frame = QFrame()
frame.setFrameShape(QFrame.HLine)
frame.setFrameShadow(QFrame.Sunken)
self.dialog_layout.addWidget(frame)
question_layout.addLayout(answer_layout)
self.dialog_layout.addLayout(question_layout)
......@@ -115,11 +115,13 @@ class DragButton(QPushButton):
signal.update()
def remove(self):
self._window.logger.info(f"Removing operation with name {self.operation.name}.")
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]):
self._window.logger.info(f"Removed signal with name: {signal.signal.name} to/from operation: {self.operation.name}.")
signal.remove()
_signals.append(signal)
......
......@@ -225,6 +225,8 @@ class Ui_main_window(object):
self.view_menu.setObjectName("view_menu")
self.run_menu = QtWidgets.QMenu(self.menu_bar)
self.run_menu.setObjectName("run_menu")
self.help_menu = QtWidgets.QMenu(self.menu_bar)
self.help_menu.setObjectName("help_menu")
main_window.setMenuBar(self.menu_bar)
self.status_bar = QtWidgets.QStatusBar(main_window)
self.status_bar.setObjectName("status_bar")
......@@ -239,6 +241,12 @@ class Ui_main_window(object):
self.actionRedo.setObjectName("actionRedo")
self.actionSimulateSFG = QtWidgets.QAction(main_window)
self.actionSimulateSFG.setObjectName("actionSimulateSFG")
self.aboutBASIC = QtWidgets.QAction(main_window)
self.aboutBASIC.setObjectName("aboutBASIC")
self.faqBASIC = QtWidgets.QAction(main_window)
self.faqBASIC.setObjectName("faqBASIC")
self.keybindsBASIC = QtWidgets.QAction(main_window)
self.keybindsBASIC.setObjectName("keybindsBASIC")
self.actionToolbar = QtWidgets.QAction(main_window)
self.actionToolbar.setCheckable(True)
self.actionToolbar.setObjectName("actionToolbar")
......@@ -249,10 +257,14 @@ class Ui_main_window(object):
self.edit_menu.addAction(self.actionRedo)
self.view_menu.addAction(self.actionToolbar)
self.run_menu.addAction(self.actionSimulateSFG)
self.help_menu.addAction(self.aboutBASIC)
self.help_menu.addAction(self.faqBASIC)
self.help_menu.addAction(self.keybindsBASIC)
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.menu_bar.addAction(self.help_menu.menuAction())
self.retranslateUi(main_window)
self.operation_list.setCurrentIndex(1)
......@@ -261,7 +273,7 @@ class Ui_main_window(object):
def retranslateUi(self, main_window):
_translate = QtCore.QCoreApplication.translate
main_window.setWindowTitle(_translate("main_window", "MainWindow"))
main_window.setWindowTitle(_translate("main_window", "B-ASIC"))
self.operation_box.setTitle(_translate("main_window", "Operations"))
self.core_operations_list.setSortingEnabled(False)
__sortingEnabled = self.core_operations_list.isSortingEnabled()
......@@ -276,7 +288,11 @@ class Ui_main_window(object):
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.help_menu.setTitle(_translate("main_window", "Help"))
self.actionSimulateSFG.setText(_translate("main_window", "Simulate SFG"))
self.aboutBASIC.setText(_translate("main_window", "About B-ASIC"))
self.faqBASIC.setText(_translate("main_window", "FAQ"))
self.keybindsBASIC.setText(_translate("main_window", "Keybinds"))
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"))
......
......@@ -5,8 +5,11 @@ This python file is the main window of the GUI for B-ASIC.
from pprint import pprint
from os import getcwd, path
import logging
logging.basicConfig(level=logging.INFO)
import sys
from about_window import AboutWindow, FaqWindow, KeybindsWindow
from drag_button import DragButton
from gui_interface import Ui_main_window
from arrow import Arrow
......@@ -25,7 +28,7 @@ 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, QInputDialog
QGraphicsProxyWidget, QInputDialog, QTextEdit
from PySide2.QtCore import Qt, QSize
from PySide2.QtGui import QIcon, QFont, QPainter, QPen, QBrush, QKeySequence
......@@ -38,7 +41,6 @@ class MainWindow(QMainWindow):
super(MainWindow, self).__init__()
self.ui = Ui_main_window()
self.ui.setupUi(self)
self.setWindowTitle(" ")
self.setWindowIcon(QIcon('small_logo.png'))
self.scene = None
self._operations_from_name = dict()
......@@ -54,6 +56,7 @@ class MainWindow(QMainWindow):
self.sfg_list = []
self.source = None
self._window = self
self.logger = logging.getLogger(__name__)
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)
......@@ -74,6 +77,14 @@ class MainWindow(QMainWindow):
self.ui.view_menu.addAction(self.check_show_names)
self.ui.actionSimulateSFG.triggered.connect(self.simulate_sfg)
self.ui.faqBASIC.triggered.connect(self.display_faq_page)
self.ui.aboutBASIC.triggered.connect(self.display_about_page)
self.ui.keybindsBASIC.triggered.connect(self.display_keybinds_page)
self.shortcut_help = QShortcut(QKeySequence("Ctrl+?"), self)
self.shortcut_help.activated.connect(self.display_faq_page)
self.logger.info("Finished setting up GUI")
self.logger.info("For questions please refer to 'Ctrl+?', or visit the 'Help' section on the toolbar.")
def init_ui(self):
self.ui.core_operations_list.itemClicked.connect(self.on_list_widget_item_clicked)
......@@ -110,11 +121,13 @@ class MainWindow(QMainWindow):
self.is_show_names = True
else:
self.is_show_names = False
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):
self.logger.info("Exiting the application.")
QApplication.quit()
def create_SFG_from_toolbar(self):
......@@ -127,8 +140,11 @@ class MainWindow(QMainWindow):
outputs.append(op.operation)
name = QInputDialog.getText(self, "Create SFG", "Name: ", QLineEdit.Normal)
self.logger.info(f"Creating SFG with name: {name[0]} from selected operations.")
sfg = SFG(inputs=inputs, outputs=outputs, name=name[0])
self.logger.info(f"Created SFG with name: {name[0]} from selected operations.")
for op in self.pressed_operations:
op.setToolTip(sfg.name)
self.sfg_list.append(sfg)
......@@ -159,6 +175,7 @@ class MainWindow(QMainWindow):
port.show()
def get_operations_from_namespace(self, namespace):
self.logger.info(f"Fetching operations from namespace: {namespace.__name__}.")
return [comp for comp in dir(namespace) if hasattr(getattr(namespace, comp), "type_name")]
def add_operations_from_namespace(self, namespace, _list):
......@@ -172,7 +189,10 @@ class MainWindow(QMainWindow):
except NotImplementedError:
pass
self.logger.info(f"Added operations from namespace: {namespace.__name__}.")
def _create_operation(self, item):
self.logger.info(f"Creating operation of type: {item.text()}.")
try:
attr_oper = self._operations_from_name[item.text()]()
attr_button = DragButton(attr_oper.graph_id, attr_oper, attr_oper.type_name().lower(), True, self)
......@@ -203,14 +223,16 @@ class MainWindow(QMainWindow):
attr_button.add_label(operation_label)
self.operationDict[attr_button] = attr_button_scene
except Exception as e:
print("Unexpected error occured: ", e)
self.logger.error(f"Unexpected error occured while creating operation: {e}.")
def _refresh_operations_list_from_namespace(self):
self.logger.info("Refreshing operation list.")
self.ui.core_operations_list.clear()
self.ui.special_operations_list.clear()
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.logger.info("Finished refreshing operation list.")
def on_list_widget_item_clicked(self, item):
self._create_operation(item)
......@@ -224,10 +246,13 @@ class MainWindow(QMainWindow):
def connectButton(self, button):
if len(self.pressed_ports) < 2:
self.logger.warn("Can't connect less than two ports. Please select more.")
return
for i in range(len(self.pressed_ports) - 1):
if isinstance(self.pressed_ports[i].port, OutputPort) and \
isinstance(self.pressed_ports[i+1].port, InputPort):
self.logger.info(f"Connecting: {self.pressed_ports[i].operation.operation.type_name()} -> {self.pressed_ports[i + 1].operation.operation.type_name()}")
line = Arrow(self.pressed_ports[i], self.pressed_ports[i + 1], self)
self.signalPortDict[line] = [self.pressed_ports[i], self.pressed_ports[i + 1]]
......@@ -244,6 +269,7 @@ class MainWindow(QMainWindow):
signal.moveLine()
def _select_all_operations(self):
self.logger.info("Selecting all operations in the workspace.")
self.pressed_operations.clear()
for button in self.operationDict.keys():
button._toggle_button(pressed=False)
......@@ -262,6 +288,7 @@ class MainWindow(QMainWindow):
def _simulate_sfg(self):
for sfg, properties in self.dialog.properties.items():
self.logger.info(f"Simulating sfg with name: {sfg.name}.")
simulation = Simulation(sfg, input_providers=properties["input_values"], save_results=properties["all_results"])
l_result = simulation.run_for(properties["iteration_count"])
......@@ -271,7 +298,9 @@ class MainWindow(QMainWindow):
print(f"{'=' * 10} /{sfg.name} {'=' * 10}")
if properties["show_plot"]:
self.plot = Plot(simulation, sfg)
self.logger.info(f"Opening plot for sfg with name: {sfg.name}.")
self.logger.info("To save the plot press 'Ctrl+S' when the plot is focused.")
self.plot = Plot(simulation, sfg, self)
self.plot.show()
def simulate_sfg(self):
......@@ -285,6 +314,19 @@ class MainWindow(QMainWindow):
# Wait for input to dialog. Kinda buggy because of the separate window in the same thread.
self.dialog.simulate.connect(self._simulate_sfg)
def display_faq_page(self):
self.faq_page = FaqWindow(self)
self.faq_page.scroll_area.show()
def display_about_page(self):
self.about_page = AboutWindow(self)
self.about_page.show()
def display_keybinds_page(self):
self.keybinds_page = KeybindsWindow(self)
self.keybinds_page.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
......
......@@ -7,7 +7,7 @@ class PropertiesWindow(QDialog):
def __init__(self, operation, main_window):
super(PropertiesWindow, self).__init__()
self.operation = operation
self.main_window = main_window
self._window = main_window
self.setWindowFlags(Qt.WindowTitleHint | Qt.WindowCloseButtonHint)
self.setWindowTitle("Properties")
......@@ -49,6 +49,7 @@ class PropertiesWindow(QDialog):
self.setLayout(self.vertical_layout)
def save_properties(self):
self._window.logger.info(f"Saving properties of operation: {self.operation.name}.")
self.operation.name = self.edit_name.text()
self.operation.label.setPlainText(self.operation.name)
if self.operation.operation_path_name == "c":
......
from PySide2.QtWidgets import QDialog, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout,\
QLabel, QCheckBox, QSpinBox, QGroupBox, QFrame, QFormLayout, QGridLayout, QSizePolicy
QLabel, QCheckBox, QSpinBox, QGroupBox, QFrame, QFormLayout, QGridLayout, QSizePolicy, QFileDialog, QShortcut
from PySide2.QtCore import Qt, Signal
from PySide2.QtGui import QIntValidator
from PySide2.QtGui import QIntValidator, QKeySequence
from matplotlib.backends import qt_compat
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
......@@ -52,10 +52,11 @@ class SimulateSFGWindow(QDialog):
x += 1
y = 0
_input_value = QLineEdit()
_input_value.setValidator(QIntValidator())
_input_value.setFixedWidth(50)
input_grid.addWidget(_input_value, x, y)
input_value = QLineEdit()
input_value.setPlaceholderText(str(i))
input_value.setValidator(QIntValidator())
input_value.setFixedWidth(50)
input_grid.addWidget(input_value, x, y)
y += 1
input_layout.addLayout(input_grid)
......@@ -93,9 +94,11 @@ class SimulateSFGWindow(QDialog):
class Plot(FigureCanvas):
def __init__(self, simulation, sfg, parent=None, width=5, height=4, dpi=100):
def __init__(self, simulation, sfg, window, parent=None, width=5, height=4, dpi=100):
self.simulation = simulation
self.sfg = sfg
self.dpi = dpi
self._window = window
fig = Figure(figsize=(width, height), dpi=dpi)
fig.suptitle(sfg.name, fontsize=20)
......@@ -106,8 +109,22 @@ class Plot(FigureCanvas):
FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
self.save_figure = QShortcut(QKeySequence("Ctrl+S"), self)
self.save_figure.activated.connect(self._save_plot_figure)
self._plot_values_sfg()
def _save_plot_figure(self):
self._window.logger.info(f"Saving plot of figure: {self.sfg.name}.")
file_choices = "PNG (*.png)|*.png"
path, ext = QFileDialog.getSaveFileName(self, "Save file", "", file_choices)
path = path.encode("utf-8")
if not path[-4:] == file_choices[-4:].encode("utf-8"):
path += file_choices[-4:].encode("utf-8")
if path:
self.print_figure(path.decode(), dpi=self.dpi)
self._window.logger.info(f"Saved plot: {self.sfg.name} to path: {path}.")
def _plot_values_sfg(self):
x_axis = list(range(len(self.simulation.results.keys())))
for _output in range(self.sfg.output_count):
......
......@@ -6,6 +6,7 @@ def handle_error(fn):
try:
return fn(self, *args, **kwargs)
except Exception as e:
self._window.logger.error(f"Unexpected error: {format_exc()}")
QErrorMessage(self._window).showMessage(f"Unexpected error: {format_exc()}")
return wrapper
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment