Skip to content

Commit

Permalink
-Include GUI in logging and messaging system
Browse files Browse the repository at this point in the history
-Log all GUI infos and warnings through logging module
  • Loading branch information
Artur Glavic committed Apr 4, 2013
1 parent 61c3bfd commit 9f8e663
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 119 deletions.
87 changes: 0 additions & 87 deletions quick_nxs/error_handling.py

This file was deleted.

107 changes: 102 additions & 5 deletions quick_nxs/gui_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import sys
import atexit
import logging
import traceback
from .version import str_version

# default options used
Expand All @@ -23,30 +24,126 @@
LOG_DIR=os.path.join(USER_DIR, 'debug.log')
if not os.path.exists(USER_DIR):
os.makedirs(USER_DIR)
ADMIN_MAIL='[email protected]'

def excepthook_overwrite(*exc_info):
logging.error('unexpected python error', exc_info=exc_info)
logging.critical('python error', exc_info=exc_info)

def goodby():
logging.debug('*** QuickNXS %s Logging ended ***'%str_version)

def setup_system():
logger=logging.root#logging.getLogger('quick_nxs')
logger.setLevel(logging.DEBUG)
console=logging.StreamHandler(sys.__stdout__)
formatter=logging.Formatter('%(levelname) 7s: %(message)s')
console.setFormatter(formatter)
console.setLevel(CONSOLE_LEVEL)

logger.addHandler(console)
logfile=logging.FileHandler(LOG_DIR, 'w')
formatter=logging.Formatter('[%(levelname)s] - %(asctime)s - %(filename)s:%(lineno)i:%(funcName)s %(message)s',
'')
logfile.setFormatter(formatter)
logfile.setLevel(FILE_LEVEL)
logger.addHandler(logfile)

logging.debug('*** QuickNXS %s Logging started ***'%str_version)

sys.excepthook=excepthook_overwrite
atexit.register(goodby)

class QtHandler(logging.Handler):
'''
A logging Handler to be used by a GUI widget to show the data.
'''
max_items=1e5
info_limit=logging.INFO
warn_limit=logging.WARNING

def __init__(self, main_window):
logging.Handler.__init__(self, level=GUI_LEVEL)
self.logged_items=[]
self.main_window=main_window

def emit(self, record):
self.logged_items.append(record)
# make sure the buffer doesn't get infinitly large
if len(self.logged_items)>self.max_items:
self.logged_items.pop(0)
if record.levelno<=self.info_limit:
self.show_info(record)
elif record.levelno<=self.warn_limit:
self.show_warning(record)
else:
self.show_error(record)

def show_info(self, record):
msg=record.msg
if record.levelno!=logging.INFO:
msg=record.levelname+': '+msg
self.main_window.ui.statusbar.showMessage(msg, 5000.)

def show_warning(self, record):
'''
Warning messages display a dialog to the user.
'''
from PyQt4.QtGui import QMessageBox
QMessageBox.warning(self.main_window, 'QuickNXS '+record.levelname, record.msg)

def show_error(self, record):
'''
More urgent error messages allow to send a bug report.
'''
from PyQt4.QtGui import QMessageBox
from PyQt4.QtCore import Qt
mbox=QMessageBox(self.main_window)
mbox.setIcon(QMessageBox.Critical)
mbox.setTextFormat(Qt.RichText)
mbox.setInformativeText('Do you want to send the logfile to the developer?')
mbox.setStandardButtons(QMessageBox.Yes|QMessageBox.No)
mbox.setDefaultButton(QMessageBox.No)

if record.exc_info:
tb=traceback.format_exception(*record.exc_info)
message='\n'.join(tb)
mbox.setDetailedText(message)
mbox.setText(u'An unexpected error has occurred: <b>%s</b><br />&nbsp;&nbsp;&nbsp;&nbsp;<i>%s</i>: %s'%(
record.msg,
record.exc_info[0].__name__,
record.exc_info[1]))
else:
mbox.setText(u'An unexpected error has occurred: <br />&nbsp;&nbsp;<b>%s</b>'%record.msg)
result=mbox.exec_()
if result==QMessageBox.Yes:
logging.info('Sending mail')
try:
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from getpass import getuser

msg=MIMEMultipart()
msg['Subject']='QuickNXS error report'
msg['From']='%[email protected]'%getuser()
msg['To']=ADMIN_MAIL
text='This is an automatic bugreport from QuickNXS\n\n%s'%record.msg
if record.exc_info:
text+='\n\n'+message
text+='\n'
msg.preamble=text
msg.attach(MIMEText(text))

mitem=MIMEText(open(LOG_DIR, 'r').read(), 'log')
mitem.add_header('Content-Disposition', 'attachment', filename='debug.log')
msg.attach(mitem)

smtp=smtplib.SMTP('160.91.4.26')
smtp.sendmail(msg['From'], msg['To'].split(','), msg.as_string())
smtp.quit()
logging.info('Mail sent')
except:
logging.warning('problem sending the mail', exc_info=True)

def install_gui_handler(main_window):
logging.root.addHandler(QtHandler(main_window))
47 changes: 22 additions & 25 deletions quick_nxs/main_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Module including main GUI class with all signal handling and plot creation.
'''

import os, sys
import os
from glob import glob
from numpy import where, pi, newaxis, log10
from cPickle import load, dump
Expand All @@ -15,11 +15,12 @@
from . import gui_utils
from .compare_plots import CompareDialog
from .advanced_background import BackgroundDialog
from .error_handling import ErrorHandler
from .mreduce import NXSData, NXSMultiData, Reflectivity, OffSpecular, time_from_header, GISANS, DETECTOR_X_REGION
from .mrcalc import get_total_reflection, get_scaling, get_xpos, get_yregion, refine_gauss

#from logging import info, debug
from logging import info, debug, warning
from .gui_logging import install_gui_handler
from .decorators import log_call, log_input, log_both

BASE_FOLDER='/SNS/REF_M'
Expand Down Expand Up @@ -127,6 +128,7 @@ def __init__(self, argv=[]):
self.auto_change_active=True
self.ui=Ui_MainWindow()
self.ui.setupUi(self)
install_gui_handler(self)
self.setWindowTitle(u'QuickNXS %s'%str_version)
self.cache_indicator=QtGui.QLabel("Cache Size: 0.0MB")
self.ui.statusbar.addPermanentWidget(self.cache_indicator)
Expand Down Expand Up @@ -170,8 +172,6 @@ def __init__(self, argv=[]):
self.ipython=IPythonConsoleQtWidget(self)
self.ui.plotTab.addTab(self.ipython, 'IPython')
else:
# catch python errors with error handling from stderr
sys.stderr=ErrorHandler(self)
self.ipython=None
if len(argv)>0:
# delay action to be run within event loop, this allows the error handling to work
Expand All @@ -184,6 +184,7 @@ def __init__(self, argv=[]):
self.trigger('loadExtraction', argv[0])
else:
self.ui.numberSearchEntry.setFocus()
self.auto_change_active=True # prevent exceptions when changing options without file open

@log_input
def processDelayedTrigger(self, item, args):
Expand Down Expand Up @@ -244,7 +245,7 @@ def fileOpen(self, filename, do_plot=True):
self._foThread.terminate()
self._foThread.wait(100)
self._foThread=None
self.ui.statusbar.showMessage(u"Reading file %s..."%(filename))
info(u"Reading file %s..."%(filename))
if self.read_with_thread:
self._foThread=fileOpenThread(self, filename)
self._foThread.finished.connect(self._fileOpenDone)
Expand Down Expand Up @@ -274,7 +275,7 @@ def fileOpenSum(self, filenames, do_plot=True):
self._foThread.terminate()
self._foThread.wait(100)
self._foThread=None
self.ui.statusbar.showMessage(u"Reading files %s..."%(filenames[0]))
info(u"Reading files %s..."%(filenames[0]))
data=NXSMultiData(filenames,
bin_type=self.ui.eventBinMode.currentIndex(),
bins=self.ui.eventTofBins.value(),
Expand Down Expand Up @@ -306,7 +307,7 @@ def _fileOpenDone(self, data=None, filename=None, do_plot=None):
self.ui.selectedChannel.setItemText(i, 'NONE')
self.active_data=data
self.last_mtime=os.path.getmtime(filename)
self.ui.statusbar.showMessage(u"%s loaded"%(filename), 5000)
info(u"%s loaded"%(filename))
self.cache_indicator.setText('Cache Size: %.1fMB'%(NXSData.get_cachesize()/1024.**2))

self.fileLoaded.emit()
Expand Down Expand Up @@ -859,7 +860,7 @@ def plot_gisans(self):
plot.clear()
plot.canvas.fig.text(0.3, 0.5, "Pease wait for calculation\nto be finished.")
plot.draw()
self.ui.statusbar.showMessage('Calculating GISANS projection...')
info('Calculating GISANS projection...')
self.updateEventReadout(0.)

options=dict(self.refl.options)
Expand All @@ -880,7 +881,7 @@ def plot_gisans(self):
def _plot_gisans(self):
gisans=self._gisansThread.gisans
self._gisansThread=None
self.ui.statusbar.showMessage('Calculating GISANS projection, Done.', 1000)
info('Calculating GISANS projection, Done.')
plots=[self.ui.gisans_pp, self.ui.gisans_mm, self.ui.gisans_pm, self.ui.gisans_mp]
Imin=10**self.ui.gisansImin.value()
Imax=10**self.ui.gisansImax.value()
Expand Down Expand Up @@ -992,7 +993,7 @@ def openByNumber(self):
Search the data folders for a specific file number and open it.
'''
number=self.ui.numberSearchEntry.text()
self.ui.statusbar.showMessage('Trying to locate file number %s...'%number)
info('Trying to locate file number %s...'%number)
QtGui.QApplication.instance().processEvents()
if self.ui.histogramActive.isChecked():
search=glob(os.path.join(BASE_FOLDER, (BASE_SEARCH%number)+u'histo.nxs'))
Expand All @@ -1004,7 +1005,7 @@ def openByNumber(self):
self.ui.numberSearchEntry.setText('')
self.fileOpen(search[0])
else:
self.ui.statusbar.showMessage('Could not locate %s...'%number, 2500)
info('Could not locate %s...'%number)

@log_call
def nextFile(self):
Expand Down Expand Up @@ -1142,8 +1143,8 @@ def automaticExtraction(self, filenames):
else:
norm=self.getNorm()
if norm is None:
QtGui.QMessageBox.warning(self, 'Automatic extraction failed',
'There is a dataset without fitting normalization, automatic extraction stopped!')
warning('There is a dataset without fitting normalization, automatic extraction stopped!',
extra={'title': 'Automatic extraction failed'})
break
# cut regions where the incident intensity drops below 10% of the maximum
region=where(norm.Rraw>=(norm.Rraw.max()*0.1))[0]
Expand Down Expand Up @@ -1399,8 +1400,8 @@ def normalizeTotalReflection(self):
Extract the scaling factor from the reflectivity curve.
'''
if self.refl is None or not self.refl.options['normalization']:
QtGui.QMessageBox.information(self, 'Select other dataset',
'Please select a dataset with total reflection plateau\nand normalization.')
warning('Please select a dataset with total reflection plateau\nand normalization.',
extra={'title': 'Select other dataset'})
return
self.auto_change_active=True
if len(self.reduction_list)>0:
Expand Down Expand Up @@ -1434,21 +1435,18 @@ def addRefList(self, do_plot=True):
if self.refl is None:
return
if self.refl.options['normalization'] is None:
QtGui.QMessageBox.information(self, u'Data not normalized',
u"You can only add reflectivities (λ normalized)!",
QtGui.QMessageBox.Close)
warning(u"You can only add reflectivities (λ normalized)!",
extra={'title': u'Data not normalized'})
return
# collect current settings
channels=self.channels
if self.reduction_list==[]:
self.ref_list_channels=list(channels)
elif self.ref_list_channels!=channels:
QtGui.QMessageBox.information(self, u'Wrong Channels',
u'''The active dataset has not the same channels
as the ones already in the list:
warning(u'''The active dataset has not the same channels as the ones already in the list:
%s ≠ %s'''%(u" / ".join(channels), u' / '.join(self.ref_list_channels)),
QtGui.QMessageBox.Close)
extra={'title': u'Wrong Channels'})
return
# options used for the extraction
opts=self.refl.options
Expand Down Expand Up @@ -1640,9 +1638,8 @@ def reduceDatasets(self):
reduction items.
'''
if len(self.reduction_list)==0:
QtGui.QMessageBox.information(self, u'Select a dataset',
u'Please select at least\none dataset to reduce.',
QtGui.QMessageBox.Close)
warning(u'Please select at least\none dataset to reduce.',
extra={'title': u'Select a dataset'})
return
dialog=gui_utils.ReduceDialog(self, self.ref_list_channels, self.reduction_list)
dialog.exec_()
Expand Down
Loading

0 comments on commit 9f8e663

Please sign in to comment.