diff --git a/qtapputils/managers/capture.py b/qtapputils/managers/capture.py index 3455736..26cfb8f 100644 --- a/qtapputils/managers/capture.py +++ b/qtapputils/managers/capture.py @@ -41,13 +41,23 @@ class StandardStreamEmitter(QObject): """ sig_new_text = Signal(str) + def __init__(self, stream='stdout'): + super().__init__() + self._stream = sys.__stderr__ if stream == 'stderr' else sys.__stdout__ + def write(self, text): try: - sys.__stdout__.write(text) + self._stream.write(text) except Exception: pass self.sig_new_text.emit(str(text)) + def flush(self): + try: + self._stream.flush() + except Exception: + pass + class SysCaptureManager(QObject): """ @@ -76,10 +86,10 @@ def __init__(self, start_capture=False): self.except_hook.sig_except_caught.connect(self._handle_except) # Setup the standard stream emitter. - self.stdout_emitter = StandardStreamEmitter() + self.stdout_emitter = StandardStreamEmitter('stdout') self.stdout_emitter.sig_new_text.connect(self.__handle_stdout) - self.stderr_emitter = StandardStreamEmitter() + self.stderr_emitter = StandardStreamEmitter('stderr') self.stderr_emitter.sig_new_text.connect(self.handle_stderr) if start_capture: diff --git a/qtapputils/widgets/exceptions.py b/qtapputils/widgets/exceptions.py index a78bf6a..ef2db1c 100644 --- a/qtapputils/widgets/exceptions.py +++ b/qtapputils/widgets/exceptions.py @@ -7,29 +7,24 @@ # Licensed under the terms of the GNU General Public License. # ----------------------------------------------------------------------------- from __future__ import annotations -from typing import TYPE_CHECKING, Callable # ---- Standard library imports import os -import os.path as osp import sys import datetime import tempfile +from pathlib import Path # ---- Third party imports from qtapputils.icons import get_standard_icon, get_standard_iconsize -from qtpy.QtCore import Qt -from qtpy.QtGui import QIcon +from qtpy.QtCore import Qt, QUrl +from qtpy.QtGui import QIcon, QDesktopServices from qtpy.QtWidgets import ( QApplication, QDialog, QDialogButtonBox, QGridLayout, QLabel, QPushButton, QTextEdit, QWidget) -# ---- Local imports -from hydrogeolab.config.main import TEMP_DIR - - class ExceptDialog(QDialog): """ A dialog to report internal errors encountered by the application during @@ -38,7 +33,8 @@ class ExceptDialog(QDialog): def __init__(self, appname: str, appver: str, system_info: str = None, icon: QIcon = None, issue_tracker: str = None, - issue_email: str = None, parent: QWidget = None): + issue_email: str = None, parent: QWidget = None, + temp_dir: str | Path = None): super().__init__(parent) self.setWindowTitle(f"{appname} Internal Error") self.setWindowFlags( @@ -46,6 +42,15 @@ def __init__(self, appname: str, appver: str, system_info: str = None, if icon is not None: self.setWindowIcon(icon) + self.temp_dir = None + if temp_dir is not None: + temp_dir = Path(temp_dir) + if temp_dir.is_dir() and os.access(temp_dir, os.W_OK): + self.temp_dir = temp_dir + else: + print(f"Provided temp_dir {temp_dir} is not a valid or " + "writable directory. Falling back to system default.") + self.log_msg = None self.detailed_log = None @@ -176,11 +181,14 @@ def show_detailed_log(self): chosen by the OS. """ name = '{}Log_{}.txt'.format(self.appname, self.log_datetime) - temp_path = tempfile.mkdtemp(dir=TEMP_DIR) - temp_filename = osp.join(temp_path, name) - with open(temp_filename, 'w') as txtfile: + + temp_path = Path(tempfile.mkdtemp(dir=self.temp_dir)) + temp_filename = temp_path / name + with open(temp_filename, 'w', encoding='utf-8') as txtfile: txtfile.write(self.detailed_log) - os.startfile(temp_filename) + + # Cross-platform file opening (Windows, macOS, Linux) via Qt + QDesktopServices.openUrl(QUrl.fromLocalFile(str(temp_filename))) def copy(self): """