diff --git a/OATFWGUI/log_utils.py b/OATFWGUI/log_utils.py index 0a19b48..a58bd46 100644 --- a/OATFWGUI/log_utils.py +++ b/OATFWGUI/log_utils.py @@ -8,11 +8,13 @@ from datetime import datetime from typing import Tuple, List, Optional -from PySide6.QtCore import Slot, Signal, QObject, QFileSystemWatcher, QFile, QMetaMethod +from PySide6.QtCore import Slot, Signal, QObject, QFileSystemWatcher, QFile, QMetaMethod, QtMsgType, QMessageLogContext, \ + qInstallMessageHandler from external_processes import get_install_dir from platform_check import get_platform, PlatformEnum from qt_extensions import get_signal +from qwarningbannerholder import global_warning_banners class LogObject(QObject): @@ -110,6 +112,7 @@ def create_file(self, file_suffix: str = '') -> Optional[str]: watch_success = self.file_watcher.addPath(self.tempfile.name) if not watch_success: self.log.warning(f'Could not watch external file: {self.tempfile.name}') + global_warning_banners.add(f'Could not watch external file: {self.tempfile.name}') return None return self.tempfile.name @@ -131,6 +134,26 @@ def stop(self): self.log.warning(f'Could not remove temp file {self.tempfile.name}') +def qt_message_handler(msg_type: QtMsgType, msg_ctx: QMessageLogContext, msg: str): + qt_log_level_map = { + QtMsgType.QtDebugMsg: logging.DEBUG, + QtMsgType.QtInfoMsg: logging.INFO, + QtMsgType.QtWarningMsg: logging.WARNING, + QtMsgType.QtCriticalMsg: logging.CRITICAL, + QtMsgType.QtFatalMsg: logging.FATAL, + } + py_log_level = qt_log_level_map.get(msg_type, logging.INFO) + + if any([msg_ctx.file, msg_ctx.function, msg_ctx.line]): + # I don't think the context is usually filled but here if needed + context_str = f' {msg_ctx.file}:{msg_ctx.function}:{msg_ctx.line}' + else: + context_str = '' + # Don't want to global the `log` variable here (idk if things will break) + log = logging.getLogger('') + log.log(py_log_level, f'Qt:{msg}{context_str}') + + def setup_logging(logger, qt_log_obj: LogObject): logger.setLevel(logging.DEBUG) # file handler @@ -153,4 +176,7 @@ def setup_logging(logger, qt_log_obj: LogObject): gh.setFormatter(CustomFormatter(colour_type=LogColourTypes.html)) logger.addHandler(gh) + # Qt logging + qInstallMessageHandler(qt_message_handler) + logger.debug(f'Logging initialized (logfile={log_file})') diff --git a/OATFWGUI/main.py b/OATFWGUI/main.py index 790585f..ebf5465 100755 --- a/OATFWGUI/main.py +++ b/OATFWGUI/main.py @@ -24,6 +24,7 @@ from gui_logic import BusinessLogic from platform_check import get_platform, PlatformEnum from external_processes import external_processes, add_external_process, get_install_dir +from qwarningbannerholder import global_warning_banners from anon_usage_data import create_anon_stats from misc_utils import delete_directory @@ -43,6 +44,7 @@ def check_and_warn_directory_path_length(dir_to_check: Path, max_path_len: int, lengths greater than the default Windows path length of 260 characters. ''' log.warning(general_warn_str + warn_str) + global_warning_banners.add(f'{dir_to_check} might have too many characters in it ({num_chars_in_dir})!') def setup_environment(): @@ -107,6 +109,7 @@ def raw_version_to_semver() -> Optional[semver.VersionInfo]: semver_ver = semver.VersionInfo.parse(__version__) except ValueError as e: log.warning(f'Could not parse my own version string {__version__} {e}') + global_warning_banners.add(f'Could not parse my own version string {__version__}') return None return semver_ver @@ -130,6 +133,7 @@ def check_new_oatfwgui_release() -> Optional[Tuple[str, str]]: release_ver = semver.VersionInfo.parse(release_json['tag_name']) except ValueError as e: log.warning(f'Could not parse tag name as semver {release_json["tag_name"]} {e}') + global_warning_banners.add(f'Could not parse tag name as semver {release_json["tag_name"]}') continue releases[release_ver] = release_json['html_url'] if latest_release_ver is None or release_ver > latest_release_ver: diff --git a/OATFWGUI/main_widget.ui b/OATFWGUI/main_widget.ui index 8ee85c7..0b0f693 100644 --- a/OATFWGUI/main_widget.ui +++ b/OATFWGUI/main_widget.ui @@ -24,6 +24,12 @@ + + + + + + @@ -31,13 +37,6 @@ - - - - What will be uploaded? - - - @@ -48,34 +47,17 @@ - - - - No port selected - - - - - - - No FW downloaded yet... - - - - - + + - Select firmware version: + Select board: - - - - false - + + - Download + What will be uploaded? @@ -89,17 +71,37 @@ - - + + - Select board: + Select firmware version: - - + + + + + - Grabbing FW Versions... + No FW downloaded yet... + + + + + + + No config file selected + + + + + + + false + + + Download @@ -116,8 +118,12 @@ - - + + + + No port selected + + @@ -136,19 +142,16 @@ - - - - - - - - - - No config file selected + + + + Grabbing FW Versions... + + + @@ -175,7 +178,11 @@ QBusyIndicatorGoodBad QWidget
qbusyindicatorgoodbad
- 1 + + + QWarningBannerHolder + QWidget +
qwarningbannerholder
diff --git a/OATFWGUI/platform_check.py b/OATFWGUI/platform_check.py index 8049c7e..28df41a 100644 --- a/OATFWGUI/platform_check.py +++ b/OATFWGUI/platform_check.py @@ -2,6 +2,8 @@ import platform import logging +from qwarningbannerholder import global_warning_banners + platform_lookup_cache = None log = logging.getLogger('') @@ -33,5 +35,6 @@ def get_platform() -> PlatformEnum: log.debug(f'platform_str={platform_str}') if platform_lookup == PlatformEnum.UNKNOWN: log.warning(f'Unknown platform {platform_str}!') + global_warning_banners.add(f'Unknown platform {platform_str}!') return platform_lookup diff --git a/OATFWGUI/qbusyindicatorgoodbad.py b/OATFWGUI/qbusyindicatorgoodbad.py index 1a3931a..6778d29 100644 --- a/OATFWGUI/qbusyindicatorgoodbad.py +++ b/OATFWGUI/qbusyindicatorgoodbad.py @@ -13,31 +13,6 @@ class BusyIndicatorState(enum.Enum): - # designer_dom_xml = ''' - # - # - # - # - # 10 - # 10 - # - # - # - # - # ''' - # - # def get_fixed_size(self) -> QSize: - # return self._fixed_size - # - # def set_fixed_size(self, fixed_size: QSize): - # self._fixed_size = fixed_size - # self.setFixedSize(fixed_size) - # - # fixed_size = Property(QSize, get_fixed_size, set_fixed_size) - # - # ... - # - # self._fixed_size = QSize(200, 200) NONE = enum.auto() BUSY = enum.auto() GOOD = enum.auto() diff --git a/OATFWGUI/qt_extensions.py b/OATFWGUI/qt_extensions.py index 6c4a775..c315d3a 100644 --- a/OATFWGUI/qt_extensions.py +++ b/OATFWGUI/qt_extensions.py @@ -55,7 +55,7 @@ class RegisteredCustomWidget(QWidget): def factory(cls: 'RegisteredCustomWidget'): if not cls.designer_tooltip: cls.designer_tooltip = f'{cls.__name__} tooltip' - if cls.designer_dom_xml: + if not cls.designer_dom_xml: cls.designer_dom_xml = f''' diff --git a/OATFWGUI/qwarningbannerholder.py b/OATFWGUI/qwarningbannerholder.py new file mode 100644 index 0000000..44b8322 --- /dev/null +++ b/OATFWGUI/qwarningbannerholder.py @@ -0,0 +1,53 @@ +from typing import List, Set + +from PySide6.QtCore import Slot, Signal, Qt, QTimer +from PySide6.QtWidgets import QVBoxLayout, QSizePolicy, QLabel +from qt_extensions import RegisteredCustomWidget + +global_warning_banners: Set[str] = set() + + +class QWarningBannerHolder(RegisteredCustomWidget): + add_warning_signal = Signal(str) + + def __init__(self, parent=None): + super().__init__(parent) + + self.label_widgets: List[QWarningLabel] = [] + + self.vbox = QVBoxLayout() + self.vbox.setAlignment(Qt.AlignHCenter) + self.setLayout(self.vbox) + + self.check_timer = QTimer(self) + self.check_timer.timeout.connect(self.check_global_warnings) + self.check_timer.setInterval(1000) # every second + self.check_timer.start() + + self.add_warning_signal.connect(self.add_warning) + + if self.running_in_designer(): + global_warning_banners.add('Test warning 1') + global_warning_banners.add('Test warning 2') + global_warning_banners.add('Test warning 3') + + def check_global_warnings(self): + # Just go through all of our labels and compare the text + # Not efficient but whatever + for warn_str in global_warning_banners: + if not any(warn_str in wid.text() for wid in self.label_widgets): + self.add_warning_signal.emit(warn_str) + + # Need to use signals and slots else we get: + # QObject::setParent: Cannot set parent, new parent is in a different thread + @Slot() + def add_warning(self, text: str): + self.label_widgets.append(QWarningLabel(text)) + self.vbox.addWidget(self.label_widgets[-1]) + + +class QWarningLabel(QLabel): + def __init__(self, text: str): + super().__init__(f'WARNING:{text}') + self.setStyleSheet('QLabel { background-color : yellow; }') + self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) diff --git a/OATFWGUI/register_custom_qt_widgets.py b/OATFWGUI/register_custom_qt_widgets.py index 8bdfbda..7ae6bf9 100644 --- a/OATFWGUI/register_custom_qt_widgets.py +++ b/OATFWGUI/register_custom_qt_widgets.py @@ -1,6 +1,7 @@ from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection from qbusyindicatorgoodbad import QBusyIndicatorGoodBad +from qwarningbannerholder import QWarningBannerHolder """ Set the environment variable PYSIDE_DESIGNER_PLUGINS to this directory and load the plugin, @@ -17,3 +18,10 @@ tool_tip=qbusyindicatorgoodbad.designer_tooltip, xml=qbusyindicatorgoodbad.designer_dom_xml, ) + qwarningbannerholder = QWarningBannerHolder.factory() + QPyDesignerCustomWidgetCollection.registerCustomWidget( + qwarningbannerholder, + module=qwarningbannerholder.designer_module, # idk what this actually affects + tool_tip=qwarningbannerholder.designer_tooltip, + xml=qwarningbannerholder.designer_dom_xml, + )