# -*- mode:Python -*- # # This file is part of the Coriolis Software. # Copyright (c) SU 2012-2020, All Rights Reserved # # +-----------------------------------------------------------------+ # | C O R I O L I S | # | Alliance / Hurricane Interface | # | | # | Author : Jean-Paul Chaput | # | E-mail : Jean-Paul.Chaput@lip6.fr | # | =============================================================== | # | Python : "./crlcore/helpers/io.py" | # +-----------------------------------------------------------------+ from __future__ import print_function import sys import os import os.path import re import traceback try: from PyQt4.QtCore import Qt from PyQt4.QtGui import QSizePolicy from PyQt4.QtGui import QDialog from PyQt4.QtGui import QPalette from PyQt4.QtGui import QColor from PyQt4.QtGui import QFont from PyQt4.QtGui import QFontMetrics from PyQt4.QtGui import QWidget from PyQt4.QtGui import QFrame from PyQt4.QtGui import QLabel from PyQt4.QtGui import QPixmap from PyQt4.QtGui import QPushButton from PyQt4.QtGui import QTextEdit from PyQt4.QtGui import QVBoxLayout from PyQt4.QtGui import QHBoxLayout from PyQt4.QtGui import QAction from PyQt4.QtGui import QKeySequence except Exception, e: try: from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QSizePolicy from PyQt5.QtWidgets import QDialog from PyQt5.QtGui import QPalette from PyQt5.QtGui import QColor from PyQt5.QtGui import QFont from PyQt5.QtGui import QFontMetrics from PyQt5.QtWidgets import QWidget from PyQt5.QtWidgets import QFrame from PyQt5.QtWidgets import QLabel from PyQt5.QtGui import QPixmap from PyQt5.QtWidgets import QPushButton from PyQt5.QtWidgets import QTextEdit from PyQt5.QtWidgets import QVBoxLayout from PyQt5.QtWidgets import QHBoxLayout from PyQt5.QtWidgets import QAction from PyQt5.QtGui import QKeySequence except e: print( '[ERROR] helpers.io, neither PyQt4 nor PyQt5 is available.' ) sys.exit( 1 ) import Cfg import helpers from Hurricane import UpdateSession import Viewer # ------------------------------------------------------------------- # Class : "ErrorWidget". class ErrorWidget ( QDialog ): def __init__ ( self, e ): QWidget.__init__ ( self, None ) self.setWindowTitle( 'Error' ) message = QLabel( e.getLinesAsString() ) message.setAlignment( Qt.AlignLeft ) message.setFont( QFont('Courier',10,QFont.Bold) ) error = QLabel( '[ERROR]' ) error.setAlignment( Qt.AlignLeft ) font = error.font() font.setWeight( QFont.Bold ) error.setFont( font ) self._tryCont = QPushButton() self._tryCont.setSizePolicy( QSizePolicy.Fixed, QSizePolicy.Fixed ) self._tryCont.setText ( 'Try to continue' ) self._abort = QPushButton() self._abort.setSizePolicy( QSizePolicy.Fixed, QSizePolicy.Fixed ) self._abort.setText ( 'Abort' ) self._abort.setDefault ( True ) traceFont = QFont('Courier',10,QFont.Normal) lineHeight = QFontMetrics( traceFont ).height() traceText = helpers.textStackTrace( e.trace, False, e.scriptPath ) lineCount = traceText.count( '\n' ) + 2 minimumWidth = 400 if Viewer.Graphics.isHighDpi(): minimumWidth = 2100 self._trace = QTextEdit() self._trace.setReadOnly ( True ); self._trace.setLineWrapMode( QTextEdit.NoWrap ); self._trace.setMinimumSize ( minimumWidth, lineCount*lineHeight ); self._trace.setFont ( traceFont ) self._trace.setText ( traceText ) buttonLayout = QHBoxLayout() buttonLayout.addStretch( 1 ) buttonLayout.addWidget ( self._tryCont ) buttonLayout.addStretch( 1 ) buttonLayout.addWidget ( self._abort ) buttonLayout.addStretch( 1 ) vLayout = QVBoxLayout() vLayout.addWidget ( error ) vLayout.addStretch( 1 ) vLayout.addWidget ( message ) vLayout.addStretch( 1 ) vLayout.addWidget ( self._trace ) vLayout.addStretch( 1 ) vLayout.addLayout ( buttonLayout ) pixmapWidth = 150 if not Viewer.Graphics.isHighDpi(): pixmapWidth = 70 pixmap = QPixmap( ':/images/angry-birds-red.png' ) pixmap = pixmap.scaledToWidth( pixmapWidth ) icon = QLabel() icon.setPixmap( pixmap ) hLayout = QHBoxLayout() hLayout.addWidget( icon ) hLayout.addLayout( vLayout ) self.setLayout( hLayout ) self._tryCont.clicked.connect( self.accept ) self._abort .clicked.connect( self.reject ) self._exitAction = QAction( '&Exit', self ) self._exitAction.setStatusTip( 'Exit Coriolis' ) self._exitAction.setShortcut ( QKeySequence('CTRL+Q') ) self._exitAction.triggered.connect( self.reject ) self.addAction( self._exitAction ) self._closeAction = QAction( '&Close', self ) self._closeAction.setStatusTip( 'Try to continue' ) self._closeAction.setShortcut ( QKeySequence('CTRL+W') ) self._closeAction.triggered.connect( self.reject ) self.addAction( self._closeAction ) return def closeEvent ( self, event ): self.setResult( QDialog.Rejected ) event.accept() return # ------------------------------------------------------------------- # Class : "ErrorMessage". class ErrorMessage ( Exception ): def __init__ ( self, code, *arguments ): self.scriptPath = None self.trace = traceback.extract_stack() self.code = code self.errors = [ 'Malformed call to ErrorMessage().' , 'args:"{}"'.format(arguments) ] if not isinstance(self.code,int): self.errors = [ 'Malformed call to ErrorMessage(), first argument (code) must be an integer.' , 'code:"{}"'.format(code) , 'args:"{}"'.format(arguments) ] return text = None if len(arguments) == 1: if isinstance(arguments[0],Exception): text = str(arguments[0]).split('\n') else: self.errors = arguments[0] elif len(arguments) > 1: sys.stdout.flush() text = list(arguments) if text: self.errors = [] while len(text[0]) == 0: del text[0] lstrip = 0 if text[0].startswith('[ERROR]'): lstrip = 8 for line in text: if line[0:lstrip] == ' '*lstrip or \ line[0:lstrip-1] == '[ERROR]': self.errors += [ line[lstrip:] ] else: self.errors += [ line.lstrip() ] sys.stdout.flush() return def __str__ ( self ): if not isinstance(self.errors,list): return "[ERROR] %s" % self.errors formatted = "\n" for i in range(len(self.errors)): if i == 0: formatted += "[ERROR] %s" % self.errors[i] else: formatted += " %s" % self.errors[i] if i+1 < len(self.errors): formatted += "\n" return formatted def getLinesAsString ( self ): if not isinstance(self.errors,list): return self.errors lines = '' for line in self.errors: lines += line + '\n' return lines def addMessage ( self, message ): if not isinstance(self.errors,list): self.errors = [ self.errors ] if isinstance(message,list): for line in message: self.errors += [ line ] else: self.errors += [ message ] return def terminate ( self ): print( self ) sys.exit( self.code ) @staticmethod def show ( code, *arguments ): e = ErrorMessage( code, *arguments ) if Viewer.Graphics.get().isEnabled(): tryCont = ErrorWidget( e ).exec_() if not tryCont: raise e else: raise e return # ------------------------------------------------------------------- # Function : "catch()". # # Try to smartly display any exception on the TTY and the graphic # display, if available. def catch ( errorObject ): if isinstance(errorObject,ErrorMessage): em = errorObject else: em = ErrorMessage( 2, errorObject ) em.trace = traceback.extract_tb( sys.exc_info()[2] ) #em.scriptPath = __file__ print( em ) print( helpers.textStackTrace( em.trace, True, em.scriptPath ) ) if Viewer.Graphics.get().isEnabled(): tryCont = ErrorWidget( em ).exec_() if UpdateSession.getStackSize() > 0: UpdateSession.close() return # ------------------------------------------------------------------- # Class : "WarningMessage". class WarningMessage ( Exception ): def __init__ ( self, message ): self._warnings = message return def __str__ ( self ): if not isinstance(self._warnings,list): return "[WARNING] %s" % self._warnings formatted = "\n" for i in range(len(self._warnings)): if i == 0: formatted += "[WARNING] %s" % self._warnings[i] else: formatted += " %s" % self._warnings[i] if i+1 < len(self._warnings): formatted += "\n" return formatted # ------------------------------------------------------------------- # Function : "vprint()". # # Small wrap around print to make use of the verbosity levels. def isVL ( level ): confLevel = 0 if Cfg.getParamBool('misc.verboseLevel1').asBool(): confLevel = 1 if Cfg.getParamBool('misc.verboseLevel2').asBool(): confLevel = 2 return confLevel >= level def vprint ( level, message ): if isVL(level): print( message ) return