diff --git a/.gitignore b/.gitignore index 0d54c09f4a8eec136e8fae2a71833814ee04105f..eeb09a4fd68f3c01161a6f6d27b204329a29f3d8 100644 --- a/.gitignore +++ b/.gitignore @@ -111,5 +111,6 @@ _b_asic_debug_log.txt .qt_for_python/ *.pyproject.user *.pyproject -*/scheduler-gui/ui/ -TODO.txt \ No newline at end of file +ui_*.py +TODO.txt +*.log \ No newline at end of file diff --git a/b_asic/scheduler-gui/__init__.py b/b_asic/scheduler-gui/__init__.py index 4633afec3b124ddb439571cb05a302f6c85ab29f..c18cf2c1978e59a4d2f0d1d7e978e4bd39cd501d 100644 --- a/b_asic/scheduler-gui/__init__.py +++ b/b_asic/scheduler-gui/__init__.py @@ -2,5 +2,10 @@ Graphical user interface for B-ASIC scheduler. """ -from b_asic.schedule-gui.main_window import * -from b_asic.schedule-gui.schedule import * \ No newline at end of file + +from .main_window import * +from .scheduler import * + +#__all__ = ['main_window', 'scheduler'] +__version__ = '0.1' +__author__ = 'Andreas Bolin' diff --git a/b_asic/scheduler-gui/logger.py b/b_asic/scheduler-gui/logger.py new file mode 100644 index 0000000000000000000000000000000000000000..3dfbbebbc092b519b2901bb0cad033aba06b3e44 --- /dev/null +++ b/b_asic/scheduler-gui/logger.py @@ -0,0 +1,80 @@ +# This Python file uses the following encoding: utf-8 +""" B-ASIC Scheduler-gui Logger Module. + +Contains a logger that logs to the console and a file using levels. It is based +on the `logging` module and has predefined levels of logging. + +Usage: +------ + + >>> import logger + >>> log = logger.getLogger() + >>> log.info('This is a log post with level INFO') + +| Function call | Level | Numeric value | +|----------------|-----------|---------------| +| debug(str) | DEBUG | 10 | +| info(str) | INFO | 20 | +| warning(str) | WARNING | 30 | +| error(str) | ERROR | 40 | +| critical(str) | CRITICAL | 50 | +| exception(str) | ERROR | 40 | + +The last `exception(str)` is used to capture exceptions output, that normally +won't be captured. +See https://docs.python.org/3/howto/logging.html for more information. +""" +from logging import Logger +import logging +import logging.handlers +import os +import sys + + +def getLogger(name: str='scheduler-gui.log', loglevel: str='DEBUG') -> Logger: + """This function creates console- and filehandler and from those, creates a logger object. + + Args: + name (str, optional): Logger-id and output filename. Defaults to 'scheduler-gui.log'. + loglevel (str, optional): The minimum level that the logger will log. Defaults to 'DEBUG'. + + Returns: + Logger: 'logging.Logger' object. + """ + + logger = logging.getLogger(name) + + # if logger 'name' already exists, return it to avoid logging duplicate + # messages by attaching multiple handlers of the same type + if logger.handlers: + return logger + # if logger 'name' does not already exist, create it and attach handlers + else: + # set logLevel to loglevel or to INFO if requested level is incorrect + loglevel = getattr(logging, loglevel.upper(), logging.DEBUG) + logger.setLevel(loglevel) + + # setup the console logger + c_fmt_date = '%T' + c_fmt = '[%(process)d] %(asctime)s %(filename)18s:%(lineno)-4s %(funcName)18s() %(levelname)-8s: %(message)s' + c_formatter = logging.Formatter(c_fmt, c_fmt_date) + c_handler = logging.StreamHandler() + c_handler.setFormatter(c_formatter) + c_handler.setLevel(logging.WARNING) + logger.addHandler(c_handler) + + # setup the file logger + f_fmt_date = '%Y-%m-%dT%T%Z' + f_fmt = '%(asctime)s %(filename)18s:%(lineno)-4s %(funcName)18s() %(levelname)-8s: %(message)s' + f_formatter = logging.Formatter(f_fmt, f_fmt_date) + f_handler = logging.FileHandler(name, mode = 'w') + f_handler.setFormatter(f_formatter) + f_handler.setLevel(logging.DEBUG) + logger.addHandler(f_handler) + + if logger.name == 'scheduler-gui.log': + logger.info('Running: %s %s', + os.path.basename(sys.argv[0]), + ' '.join(sys.argv[1:])) + + return logger diff --git a/b_asic/scheduler-gui/main_window.py b/b_asic/scheduler-gui/main_window.py index 33e82c58d91da0b49220ba0cf5dd53855d2d5e10..2b53cb3d1761934491df842dd6ea8640b4813ae3 100644 --- a/b_asic/scheduler-gui/main_window.py +++ b/b_asic/scheduler-gui/main_window.py @@ -13,6 +13,7 @@ from pathlib import Path from typing import Any #from matplotlib.pyplot import bar #from diagram import * +import logger import qtpy from qtpy import uic, QtCore, QtGui, QtWidgets @@ -33,6 +34,8 @@ from qtpy.QtCore import ( QRect) +log = logger.getLogger() + # Debug struff if __debug__: # Print some system version information @@ -50,16 +53,19 @@ if __debug__: # Autocompile the .ui form to a python file. try: # PyQt5, try autocompile from qtpy.uic import compileUiDir - def _map_func(dir: str, file: str) -> tuple[str, str]: - file_base,_ = os.path.splitext(file) - return (dir+'/ui/', file_base+'_ui.py') - uic.compileUiDir('.', False, _map_func) + # def _map_func(dir: str, file: str) -> tuple[str, str]: + # file_base,_ = os.path.splitext(file) + # return (dir+'/ui/', file_base+'_ui.py') + # uic.compileUiDir('.', map=_map_func) + + uic.compileUiDir('.', map=(lambda dir,file: dir, 'ui_' + file)) except: try: # PySide2, try manual compile import subprocess - OS = sys.platform - if OS.startswith('linux'): - cmds = ["mkdir -p ui", "pyside2-uic -o ui/main_window_ui.py main_window.ui"] + os_ = sys.platform + if os_.startswith('linux'): + cmds = ["mkdir -p ui", + "pyside2-uic -o ui_main_window.py main_window.ui"] for cmd in cmds: subprocess.call(cmd.split()) else: @@ -67,14 +73,14 @@ if __debug__: raise SystemExit except: # Compile failed, look for pre-compiled file try: - from ui.main_window_ui import Ui_MainWindow + from ui_main_window import Ui_MainWindow except: # Everything failed, exit - print("ERROR: Could not import 'Ui_MainWindow'.") - print("ERROR: Can't autocompile under", QT_API, "eviroment. Try to manual compile 'main_window.ui' to 'ui/main_window_ui.py'") + log.exception("Could not import 'Ui_MainWindow'.") + log.exception("Can't autocompile under", QT_API, "eviroment. Try to manual compile 'main_window.ui' to 'ui/main_window_ui.py'") os._exit(1) -# Only availible when the form is compiled -from ui.main_window_ui import Ui_MainWindow + +from ui_main_window import Ui_MainWindow # Only availible when the form is compiled @@ -84,17 +90,16 @@ class MainWindow(QMainWindow, Ui_MainWindow): _settings: QSettings - def __init__(self): """Initialize Schedule-gui.""" super(MainWindow, self).__init__() self._init_ui() self._init_graphics_view() - + + def _init_ui(self) -> None: """Initialize the ui""" - self._settings = QSettings() self.setupUi(self) @@ -103,7 +108,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.menu_load_sfg.triggered.connect(self.open) self.menu_save_schedule.triggered.connect(self.save) self.menu_quit.triggered.connect(self.close) - self.menu_node_info.triggered.connect(self._toggle_component_info) + self.menu_node_info.triggered.connect(self.toggle_component_info) self.splitter_center.splitterMoved.connect(self._splitter_center_moved) # Setup event member functions @@ -121,7 +126,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.splitter_center.setCollapsible(1, True) def _init_graphics_view(self) -> None: - """Initialize the QGraphics""" + """Initialize the QGraphics framework""" self.graphic_scene = QGraphicsScene(self) self.graphic_view.setScene(self.graphic_scene) self.graphic_view.setRenderHint(QPainter.Antialiasing) @@ -153,7 +158,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.update_statusbar(self.tr('Schedule saved successfully')) @Slot(bool) - def _toggle_component_info(self, checked: bool) -> None: + def toggle_component_info(self, checked: bool) -> None: """This method toggles the right hand side info window.""" widths = list(self.splitter_center.sizes()) max_range = widths[0] + widths[1] diff --git a/b_asic/scheduler-gui/schedule-demo.py b/b_asic/scheduler-gui/tests/schedule-demo.py similarity index 100% rename from b_asic/scheduler-gui/schedule-demo.py rename to b_asic/scheduler-gui/tests/schedule-demo.py