def new_qt_application(app_config: dict, logger: Logger, quit_on_last_closed: bool = False, name: str = None) -> QApplication: app = QApplication(sys.argv) app.setQuitOnLastWindowClosed( quit_on_last_closed ) # otherwise windows opened through the tray icon kill the application when closed app.setApplicationName(name if name else __app_name__) app.setApplicationVersion(__version__) app.setWindowIcon(util.get_default_icon()[1]) if app_config['ui']['qt_style']: app.setStyle(str(app_config['ui']['qt_style'])) else: app.setStyle('fusion') app.setProperty('qt_style', app.style().objectName().lower()) theme_key = app_config['ui']['theme'].strip( ) if app_config['ui']['theme'] else None set_theme(theme_key=theme_key, app=app, logger=logger) if not app_config['ui']['system_theme']: app.setPalette(app.style().standardPalette()) return app
def main(): try: create_tables() app = QApplication(sys.argv) app_theme = "cvstudio" app.setProperty("theme", app_theme) if app_theme == "light": light_theme(app) elif app_theme == "dark": dark_theme(app) elif app_theme == "gray": gray_theme(app) elif app_theme == "cvstudio": cvstudio_theme(app) mainWindow = MainWindow() mainWindow.setWindowIcon(GUIUtilities.get_icon("polygon.png")) mainWindow.show() sys.exit(app.exec_()) except Exception as ex: print(ex)
class Application: _app = None # type: QApplication @property def app(self) -> QApplication: if self._app is None: self.setup() return self._app def setup(self) -> None: # Imports .qrc resources even if it is "unused" from genial.resources import icons_rc from genial.resources import locale_rc from genial.resources import plugins_rc # Initializes the app variable self._app = QApplication(sys.argv) self._app.setApplicationName("Génial") self._app.setApplicationDisplayName("Génial") self._app.setProperty("AA_EnableHighDpiScaling", True) # Qt translation qt_translator = QTranslator() if qt_translator.load(QLocale(), "qt", "_", ":/locale"): # noinspection PyArgumentList,PyCallByClass,PyTypeChecker QCoreApplication.installTranslator(qt_translator) # App translation genial_translator = QTranslator() if genial_translator.load(QLocale(), "genial", "_", ":/locale"): # noinspection PyArgumentList,PyCallByClass,PyTypeChecker QCoreApplication.installTranslator(genial_translator) def run(self) -> int: # Importing here makes available PyQt5.QtWidgets.QApplication.instance() # for each of these modules (and for their imports too.) from genial.controllers.maincontroller import MainController main_controller = MainController(self.app) main_controller.start() return self.app.exec()
dark_palette.setColor(QPalette.Disabled, QPalette.Base, QColor(68, 68, 68, 255)) dark_palette.setColor(QPalette.Disabled, QPalette.Shadow, QColor(0, 0, 0, 255)) app.setPalette(dark_palette) app.setStyleSheet( "QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }" ) if __name__ == "__main__": try: create_tables() app = QApplication(sys.argv) app_theme = "cvstudio" app.setProperty("theme", app_theme) if app_theme == "light": light_theme(app) elif app_theme == "dark": dark_theme(app) elif app_theme == "gray": gray_theme(app) elif app_theme == "cvstudio": cvstudio_theme(app) mainWindow = MainWindow() mainWindow.setWindowIcon(GUIUtilities.get_icon("polygon.png")) mainWindow.show() sys.exit(app.exec_()) except Exception as ex: print(ex)
class Main(QObject): def __init__(self, parent = None): """Create a wizard or the mainwindow""" self._parent = parent super().__init__(self._parent) print("runner/Main parent: ", self._parent, " -> self: ", self) if oPB.PRINTHIER else None self.logger = None self.args = self.get_args() self._log_level = None self._log_file = None self.translator = None # make it really quiet, part 1 if self.args.quiet: self.args.nogui = True # instantiate configuration class confighandler.ConfigHandler(oPB.CONFIG_INI) # redirect system exception hook if not self.args.noexcepthook: sys.excepthook = self.excepthook # pre-instantiating the application, avoid some nasty OpenGL messages QApplication.setAttribute(QtCore.Qt.AA_UseOpenGLES) # create new application and install stylesheet self.app = QApplication(sys.argv) self.install_stylesheet() # Create and display the splash screen, if in ui mode if not self.args.nogui: splash_pix = QPixmap(':/images/splash.png') self.splash = QSplashScreen(splash_pix, QtCore.Qt.WindowStaysOnTopHint) self.splash.setMask(splash_pix.mask()) # splash.showMessage("opsi Package Builder " + oPB.PROGRAM_VERSION + " " + translate("Main", "is loading..."), QtCore.Qt.AlignCenter, QtCore.Qt.white) self.splash.show() self.app.processEvents() # Application name self.app.setOrganizationName("opsi Package Builder") self.app.setApplicationName("opsi Package Builder " + oPB.PROGRAM_VERSION) # save ourselves in main instance property to be easily accessd via qApp # i.e. # from PyQt5.QtWidgets import qApp # main = qApp.property("main") self.app.setProperty("main", self) if confighandler.ConfigHandler.cfg.log_always == "True": if self.args.log_file is not None: self._log_file = self.args.log_file else: self._log_file = confighandler.ConfigHandler.cfg.log_file if self.args.log_level.upper() != "NOTSET": self._log_level = self.args.log_level.upper() else: self._log_level = confighandler.ConfigHandler.cfg.log_level else: self._log_file = self.args.log_file if self.args.log_level.upper() == "NOTSET": self._log_level = "CRITICAL" else: self._log_level = self.args.log_level.upper() if self._log_file is not None: if not pathlib.Path(self._log_file).is_absolute(): if platform.system() == "Windows": self._log_file = str(pathlib.PurePath(oPB.WIN_TMP_PATH, self._log_file)) if platform.system() in ["Darwin", "Linux"]: self._log_file = str(pathlib.PurePath(oPB.UNIX_TMP_PATH, self._log_file)) # overwrite development directory from config with command line arg if self.args.dev_dir is not None: confighandler.ConfigHandler.cfg.dev_dir = self.args.dev_dir # Initialize the logger and reroute QtCore messages to it self.logWindow = oPB.gui.logging.LogDialog(None, self, self._log_level) self.instantiate_logger(False) QtCore.qInstallMessageHandler(self.qt_message_handler) # log program version and user self.logger.info(80 * "-") self.logger.info("opsi PackageBuilder (MIT licensed) " + oPB.PROGRAM_VERSION) self.logger.info("Current user: "******"Command line arguments given:") for key, val in vars(self.args).items(): self.logger.info(" " + key + ": " + str(val)) if self._log_level not in ["DEBUG", "INFO", "SSHINFO", "WARNING", "ERROR", "CRITICAL", "SSH"]: self.logger.error(" Undefined log level: " + self._log_level) self.logger.error(" Log level has been set to ERROR") for elem in self.app.libraryPaths(): self.logger.debug("QT5 library path: " + elem) # write config to log, if necessary confighandler.ConfigHandler.cfg.log_config() self.check_online_status() # ----------------------------------------------------------------------------------------- # main ui dispatching # startup gui variant if not self.args.nogui: # hide console window, but only under Windows and only if app is frozen if sys.platform.lower().startswith('win'): if getattr(sys, 'frozen', False): hideConsole() # installing translators self.translator = Translator(self.app, "opsipackagebuilder") self.translator.install_translations(confighandler.ConfigHandler.cfg.language) # retranslate logWindow, as it is loaded before the translations self.logWindow.retranslateUi(self.logWindow) # create app icon app_icon = QtGui.QIcon() app_icon.addFile(':images/prog_icons/opb/package_16x16.png', QtCore.QSize(16, 16)) app_icon.addFile(':images/prog_icons/opb/package_24x24.png', QtCore.QSize(24, 24)) app_icon.addFile(':images/prog_icons/opb/package_32x32.png', QtCore.QSize(32, 32)) app_icon.addFile(':images/prog_icons/opb/package_48x48.png', QtCore.QSize(48, 48)) app_icon.addFile(':images/prog_icons/opb/package_64x64.png', QtCore.QSize(64, 64)) app_icon.addFile(':images/prog_icons/opb/package_92x92.png', QtCore.QSize(92, 92)) app_icon.addFile(':images/prog_icons/opb/package_128x128.png', QtCore.QSize(128, 128)) app_icon.addFile(':images/prog_icons/opb/package_256x256.png', QtCore.QSize(256, 256)) self.app.setProperty("prog_icon",app_icon) # startup program window self.mainWindow = main.MainWindowController(self.args) self.mainWindow.ui.showLogRequested.connect(self.logWindow.show) self.mainWindow.closeAppRequested.connect(self.logWindow.close) self.splash.finish(self.mainWindow.ui) # check for updates if configured if confighandler.ConfigHandler.cfg.updatecheck == "True": self.mainWindow.update_check() # run main app loop self.app.exec_() # only process commandline else: self.logger.info("No-GUI parameter set") # startup program window self.console = console.ConsoleController(self.args) # ----------------------------------------------------------------------------------------- # unmount drive (if exist) after end of program if (oPB.NETDRV is not None) and oPB.NETDRV != "offline": ret = MapDrive.unMapDrive(oPB.NETDRV) if ret[0] != 0: self.logger.error("Error unmounting path: " + str(ret)) else: self.logger.info("Network drive successfully unmounted") # exit and set return code self.logger.info("Exit code: " + str(oPB.EXITCODE)) # show console window if not self.args.nogui: if sys.platform.lower().startswith('win'): if getattr(sys, 'frozen', False): showConsole() sys.exit(oPB.EXITCODE) def qt_message_handler(self, mode, context, message): if mode == QtCore.QtInfoMsg: mode = 'INFO' fun = self.logger.info elif mode == QtCore.QtWarningMsg: mode = 'WARNING' fun = self.logger.warning elif mode == QtCore.QtCriticalMsg: mode = 'CRITICAL' fun = self.logger.critical elif mode == QtCore.QtFatalMsg: mode = 'FATAL' fun = self.logger.error else: mode = 'DEBUG' fun = self.logger.debug fun('Qt framework message handler: line: %d, func: %s(), file: %s' % ( context.line, context.function, context.file)) fun('Qt framework message handler: %s: %s' % (mode, message)) def install_stylesheet(self): if platform.system() == "Darwin": css = os.environ['OPB_BASE'] + "/ui/stylesheet-mac.qss" else: css = os.environ['OPB_BASE'] + "/ui/stylesheet.qss" try: with open(css, "r", encoding="utf-8", newline="\n") as file: style = file.readlines() file.close() except: self.logger.debug("Stylesheet could not be opened.") else: self.app.setStyleSheet(("\n").join(style)) def check_online_status(self): self.logger.debug("Check online status") # check if server is generally available # use SSH port for connection test ret = Helper.test_port(confighandler.ConfigHandler.cfg.opsi_server, confighandler.ConfigHandler.cfg.sshport, 0.5) if ret is True: # check network access and mount network drive if on linux if sys.platform == 'win32': self.logger.info("System platform: "+ sys.platform) if self.args.nonetdrive is False: if confighandler.ConfigHandler.cfg.usenetdrive == "False": drives = Helper.get_available_drive_letters() if not drives: self.logger.error("No free drive letter found") else: self.logger.info("Free drive letter found: " + repr(drives)) self.logger.info("Using drive letter: " + drives[::-1][0]) path = "\\\\" + confighandler.ConfigHandler.cfg.opsi_server + "\\" + "opsi_workbench" self.logger.info("Trying to mount path: " + path) ret = MapDrive.mapDrive(drives[::-1][0] + ":", path, confighandler.ConfigHandler.cfg.opsi_user, confighandler.ConfigHandler.cfg.opsi_pass) if ret[0] != 0: self.logger.error("Error mounting path: " + str(ret)) else: self.logger.info("Network drive successfully mounted") oPB.NETDRV = drives[::-1][0] + ":" else: self.logger.info("Using existing network drive") else: self.logger.info("Mounting of network drive via command line disabled") else: self.logger.info("System platform: "+ sys.platform) self.logger.warning("This is not a windows based system. No network drive will be associated") self.logger.warning("Please take care, that the specified development base path is correct.") else: self.logger.warning("opsi server not available. Offline mode activated.") self.logger.warning("Return value from connection test: " + str(ret)) oPB.NETMODE = "offline" def get_args(self): # get cmdline args cmd_params = CommandLine() return cmd_params.getArgs() def set_log_level(self, level, handler): # set log level if level == "DEBUG": handler.setLevel(logging.DEBUG) elif level == "INFO": handler.setLevel(logging.INFO) elif level == "WARNING": handler.setLevel(logging.WARNING) elif level == "ERROR": handler.setLevel(logging.ERROR) elif level == "CRITICAL": handler.setLevel(logging.WARNING) elif level == "SSH": handler.setLevel(oPB.core.logging.SSH) elif level == "SSHINFO": handler.setLevel(oPB.core.logging.SSHINFO) else: handler.setLevel(logging.ERROR) def instantiate_logger(self, long): # for additional log facilities, see # http://stackoverflow.com/questions/2183233/how-to-add-a-custom-loglevel-to-pythons-logging-facility # Create logger logger = logging.getLogger('oPB') # to add a further logging facility, we have to subclass current logger class # and set this new one to be the default logger class setLoggerClass(oPB.core.logging.SSHLogger) # log level for logger(!) - we filter on handler-based log level later self.set_log_level("DEBUG", logger) # make it really quiet, part 2 if self.args.quiet: self.noop = logging.NullHandler() logger.addHandler(self.noop) else: if long: format = logging.Formatter(oPB.LOG_LONG, oPB.LOG_DATETIME) else: format = logging.Formatter(oPB.LOG_SHORT, oPB.LOG_DATETIME) # redirect stdout / stdin if gui if not self.args.nogui: # Output forwarding sys.stdout = oPB.core.logging.LogOutput(self.logWindow.editOutput, sys.__stdout__, self.logWindow.editOutput.textColor()) sys.stderr = oPB.core.logging.LogOutput(self.logWindow.editOutput, sys.__stderr__, QtGui.QColor(QtCore.Qt.red)) # Create standart output handler self.stdout = logging.StreamHandler(sys.__stderr__) self.set_log_level(self._log_level, self.stdout) self.stdout.setFormatter(format) # Add handlers to logger logger.addHandler(self.stdout) # Create different log window handler if not self.args.nogui: # output standard msg into dialog tab "Logging" self.dialogHandler = oPB.core.logging.LogStreamHandler(self.logWindow.editLog, self) self.dialogHandler.colors = oPB.OPB_LOG_COLORS self.dialogHandler.colorize = True self.set_log_level(self._log_level, self.dialogHandler) self.dialogHandler.setFormatter(format) logger.addHandler(self.dialogHandler) # Create special SSH output log facility, put this into tab "SSH Output" self.sshHandler = oPB.core.logging.LogStreamHandler(self.logWindow.editSSH, self) self.set_log_level("SSHINFO", self.sshHandler) sshformat = logging.Formatter(oPB.LOG_SSH, oPB.LOG_DATETIME) self.sshHandler.setFormatter(sshformat) logger.addHandler(self.sshHandler) # Create log file handler, possible try: if self._log_file is not None: self.fileHandler = logging.FileHandler(self._log_file) self.set_log_level(self._log_level, self.fileHandler) self.fileHandler.setFormatter(format) logger.addHandler(self.fileHandler) except IOError as error: logger.error("Log file could not be opened: " + self._log_file) logger.error(error) self.logger = logger def excepthook(self, excType, excValue, tracebackobj): """ Global function to catch unhandled exceptions. Writes traceback to temporary file. Adopted to Python 3 from here: http://www.riverbankcomputing.com/pipermail/pyqt/2009-May/022961.html :param excType: exception type :param excValue: exception value :param tracebackobj: traceback object """ try: self.splash.close() except: pass self.logger.debug("Entering excepthook") separator = '-' * 80 logFile = tempfile.NamedTemporaryFile(mode = "w+", suffix = ".log", prefix = "opb-error-", delete = False) # """If you have any additional, confidential information you can\n""" \ # """use this mail address: <%s>\n\n""" \ notice = \ """An unhandled exception occurred. Please report the problem\n"""\ """via the official opsi PackageBuilder forum:\n\n"""\ """https://forum.opsi.org/viewforum.php?f=22\n\n"""\ """Thank you!\n\n"""\ """A log has been written to "%s".\n\nError information:\n""" % \ (logFile.name) versionInfo="0.0.1" timeString = datetime.datetime.now().strftime("%Y-%m-%d, %H:%M:%S") tbinfofile = StringIO() traceback.print_tb(tracebackobj, None, tbinfofile) tbinfofile.seek(0) tbinfo = tbinfofile.read() errmsg = '%s: \n%s' % (str(excType), str(excValue)) sections = [separator, timeString, separator, errmsg, separator, tbinfo] msg = '\n'.join(sections) try: logFile.file.write(msg) logFile.file.write(versionInfo) logFile.close() except IOError: pass if not self.args.nogui: errorbox = QtWidgets.QMessageBox() errorbox.setText(str(notice) + str(msg) + str(versionInfo)) errorbox.exec_() else: print(str(notice) + str(msg) + str(versionInfo))
class Main(QObject): def __init__(self, parent = None): """Create a wizard or the mainwindow""" super().__init__(parent) self.logger = None self.args = self.get_args() # redirect system exception hook sys.excepthook = self.excepthook # create new application self.app = QApplication(sys.argv) # Application name self.app.setOrganizationName("opsi Package Builder") self.app.setApplicationName("opsi Package Builder " + oPB.PROGRAM_VERSION) # save ourselves in main instance property to be easily accessd via qApp # i.e. # from PyQt5.QtWidgets import qApp # main = qApp.property("main") self.app.setProperty("main", self) # Initialize the logger self.logWindow = oPB.gui.logging.LogDialog(None, self, self.args.log_level.upper()) self.instantiate_logger(False) #self.instantiate_logger_old() # instantiate configuration class confighandler.ConfigHandler(oPB.CONFIG_INI) # log program version and user self.logger.info("opsi PackageBuilder (MIT licensed) " + oPB.PROGRAM_VERSION) self.logger.info("Current user: "******"Command line arguments given:") for key, val in vars(self.args).items(): self.logger.info(" " + key + ": " + str(val)) if self.args.log_level.upper() not in ["DEBUG", "INFO", "SSHINFO", "WARNING", "ERROR", "CRITICAL", "SSH"]: self.logger.error(" Undefined log level: " + self.args.log_file.upper()) self.logger.error(" Log level has been set to ERROR") for elem in self.app.libraryPaths(): self.logger.debug("QT5 library path: " + elem) self.install_translations() self.check_online_status() # ----------------------------------------------------------------------------------------- # main ui dispatching # startup gui variant if not self.args.nogui: # startup program window self.mainWindow = main.MainWindowController(self.args) self.mainWindow.ui.showLogRequested.connect(self.logWindow.show) self.mainWindow.closeAppRequested.connect(self.logWindow.close) # run main app loop self.app.exec_() # only process commandline else: self.logger.info("No-GUI parameter set") # startup program window self.console = console.ConsoleController(self.args) # ----------------------------------------------------------------------------------------- # unmount drive after end of program if oPB.NETDRV is not None: ret = MapDrive.unMapDrive(oPB.NETDRV) if ret[0] != 0: self.logger.error("Error unmounting path: " + str(ret)) else: self.logger.info("Network drive successfully unmounted") # exit and set return code self.logger.debug("Exit code: " + str(oPB.EXITCODE)) sys.exit(oPB.EXITCODE) def install_translations(self): # get current system language and load translation # we need two translators: one for the individual appplication strings # and one for the standard qt message texts # qm = 'opsiPackageBuilder_en_EN.qm' # get files qm_app = ':locale/opsiPackageBuilder_%s.qm' % QtCore.QLocale().system().name() self.logger.debug("Load application translation: " + qm_app) qm_qt = ':locale/qtbase_%s.qm' % QtCore.QLocale().system().name() self.logger.debug("Load Qt standard translation: " + qm_qt) # create translators translator_app = QtCore.QTranslator(self.app) translator_app.load(qm_app) translator_qt = QtCore.QTranslator(self.app) translator_qt.load(qm_qt) # install translators to use it later self.app.installTranslator(translator_app) self.app.installTranslator(translator_qt) def check_online_status(self): # check if server is generally available # use SSH port for connection test ret = Helper.test_port(confighandler.ConfigHandler.cfg.opsi_server, confighandler.ConfigHandler.cfg.sshport, 0.5) if type(ret) != tuple: # check network access and mount network drive if on linux if sys.platform == 'win32': self.logger.info("System platform: "+ sys.platform) if confighandler.ConfigHandler.cfg.usenetdrive == "False": drives = Helper.get_available_drive_letters() if not drives: self.logger.error("No free drive letter found") else: self.logger.info("Free drive letter found: " + repr(drives)) self.logger.info("Using drive letter: " + drives[::-1][0]) path = "\\\\" + confighandler.ConfigHandler.cfg.opsi_server + "\\" + "opsi_workbench" self.logger.info("Trying to mount path: " + path) ret = MapDrive.mapDrive(drives[::-1][0] + ":", path, confighandler.ConfigHandler.cfg.opsi_user, confighandler.ConfigHandler.cfg.opsi_pass) if ret[0] != 0: self.logger.error("Error mounting path: " + str(ret)) else: self.logger.info("Network drive successfully mounted") oPB.NETDRV = drives[::-1][0] + ":" else: self.logger.info("System platform: "+ sys.platform) self.logger.warning("This is not a windows based system. No network drive will be associated") self.logger.warning("Please take care, if the specified development base path is correct.") else: self.logger.warning("opsi server not available. Offline mode activated.") self.logger.warning("Return value from connection test: " + str(ret)) oPB.NETMODE = "offline" def get_args(self): # get cmdline args cmd_params = CommandLine() return cmd_params.getArgs() def set_log_level(self, level, handler): # set log level if level == "DEBUG": handler.setLevel(logging.DEBUG) elif level == "INFO": handler.setLevel(logging.INFO) elif level == "WARNING": handler.setLevel(logging.WARNING) elif level == "ERROR": handler.setLevel(logging.ERROR) elif level == "CRITICAL": handler.setLevel(logging.WARNING) elif level == "SSH": handler.setLevel(oPB.core.logging.SSH) elif level == "SSHINFO": handler.setLevel(oPB.core.logging.SSHINFO) else: handler.setLevel(logging.ERROR) def instantiate_logger(self, long): # for additional log facilities, see # http://stackoverflow.com/questions/2183233/how-to-add-a-custom-loglevel-to-pythons-logging-facility # Create logger logger = logging.getLogger('oPB') # to add a further logging facility, we have to subclass current logger class # and set this new one to be the default logger class setLoggerClass(oPB.core.logging.SSHLogger) # log level from command line or standard self.set_log_level(self.args.log_level.upper(), logger) if long: format = logging.Formatter(oPB.LOG_LONG, oPB.LOG_DATETIME) else: format = logging.Formatter(oPB.LOG_SHORT, oPB.LOG_DATETIME) # redirect stdout / stdin if gui if not self.args.nogui: # Output forwarding sys.stdout = oPB.core.logging.LogOutput(self.logWindow.editOutput, sys.__stdout__, self.logWindow.editOutput.textColor()) sys.stderr = oPB.core.logging.LogOutput(self.logWindow.editOutput, sys.__stderr__, QtGui.QColor(QtCore.Qt.red)) # Create standart output handler self.stdout = logging.StreamHandler(sys.__stderr__) self.set_log_level(self.args.log_level.upper(), self.stdout) self.stdout.setFormatter(format) # Add handlers to logger logger.addHandler(self.stdout) # Create different log window handler if not self.args.nogui: # output standard msg into dialog tab "Logging" self.dialogHandler = oPB.core.logging.LogStreamHandler(self.logWindow.editLog, self) self.set_log_level(self.args.log_level.upper(), self.dialogHandler) self.dialogHandler.setFormatter(format) logger.addHandler(self.dialogHandler) # Create special SSH output log facility, put this into tab "SSH Output" self.sshHandler = oPB.core.logging.LogStreamHandler(self.logWindow.editSSH, self) self.set_log_level(oPB.core.logging.SSH, self.sshHandler) sshformat = logging.Formatter(oPB.LOG_SSH, oPB.LOG_DATETIME) self.sshHandler.setFormatter(sshformat) logger.addHandler(self.sshHandler) # Create log file handler, possible try: if self.args.log_file is not None: self.fileHandler = logging.FileHandler(self.args.log_file) self.fileHandler.setFormatter(format) logger.addHandler(self.fileHandler) except IOError as error: logger.error("Log file could not be opened: " + self.args.log_file) logger.error(error) self.logger = logger def excepthook(self, excType, excValue, tracebackobj): """ Global function to catch unhandled exceptions. Writes traceback to temporary file. Adopted to Python 3 from here: http://www.riverbankcomputing.com/pipermail/pyqt/2009-May/022961.html :param excType exception type :param excValue exception value :param tracebackobj traceback object """ self.logger.debug("Entering excepthook") separator = '-' * 80 logFile = tempfile.NamedTemporaryFile(mode = "w+", suffix = ".log", prefix = "opb-error-", delete = False) notice = \ """An unhandled exception occurred. Please report the problem\n"""\ """using an error reporting email to <%s>.\n\n"""\ """A log has been written to "%s".\n\nError information:\n""" % \ (oPB.__email__, logFile.name) versionInfo="0.0.1" timeString = datetime.datetime.now().strftime("%Y-%m-%d, %H:%M:%S") tbinfofile = StringIO() traceback.print_tb(tracebackobj, None, tbinfofile) tbinfofile.seek(0) tbinfo = tbinfofile.read() errmsg = '%s: \n%s' % (str(excType), str(excValue)) sections = [separator, timeString, separator, errmsg, separator, tbinfo] msg = '\n'.join(sections) try: logFile.file.write(msg) logFile.file.write(versionInfo) logFile.close() except IOError: pass if not self.args.nogui: errorbox = QtWidgets.QMessageBox() errorbox.setText(str(notice) + str(msg) + str(versionInfo)) errorbox.exec_() else: print(str(notice) + str(msg) + str(versionInfo))