""" CCP4TextViewer.py: CCP4 GUI Project Copyright (C) 2009-2010 University of York This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3, modified in accordance with the provisions of the license to address the requirements of UK law. You should have received a copy of the modified GNU Lesser General Public License along with this library. If not, copies may be downloaded from http://www.ccp4.ac.uk/ccp4license.php This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. """ """ Liz Potterton Jan 2010 - Create CCP4AbstractViewer """ ##@package CCP4TextViewer (QtGui) Web browser plugin to view text files and coordinate files from PyQt4 import QtGui,QtCore import CCP4AbstractViewer from CCP4ErrorHandling import * from CCP4Modules import MIMETYPESHANDLER,QTAPPLICATION,PREFERENCES class CTextBrowser(QtGui.QTextBrowser): def __init__(self,parent=None): QtGui.QTextBrowser.__init__(self,parent) def mouseReleaseEvent(self,event): self.emit(QtCore.SIGNAL('mouseRelease'),event.globalPos()) event.accept() def mouseDoubleClickEvent(self,event): self.emit(QtCore.SIGNAL('mouseDoubleClick'),event.globalPos()) event.accept() #------------------------------------------------------------------- #------------------------------------------------------------------- #------------------------------------------------------------------- class CTextViewer(CCP4AbstractViewer.CAbstractViewer): #------------------------------------------------------------------- #------------------------------------------------------------------- #------------------------------------------------------------------- MENUTEXT = 'Display text' ERROR_CODES = {} ERROR_CODES.update(CCP4AbstractViewer.CAbstractViewer.ERROR_CODES) ERROR_CODES.update( { 101 : { 'severity' : SEVERITY_ERROR, 'description' : 'Error loading text into document' } } ) # Display plain text using the QTextBrowser widget. # Could alternatively us the QWebView which seems to handle text OK #------------------------------------------------------------------- def __init__(self,parent,fileName=None): #------------------------------------------------------------------- #print 'TextViewer.init',self,fileName CCP4AbstractViewer.CAbstractViewer.__init__(self,parent) layout = QtGui.QVBoxLayout() layout.setSpacing(0) layout.setContentsMargins(0,0,0,0) #self.viewer = QtGui.QTextBrowser(self) self.viewer = CTextBrowser(self) self.connect(self.viewer,QtCore.SIGNAL('mouseRelease'),self.handleMouseClick) self.connect(self.viewer,QtCore.SIGNAL('mouseDoubleClick'),self.handleMouseDoubleClick) layout.addWidget(self.viewer) self.fixedWidthFont = False self.document = None self.setLayout(layout) if fileName: self.open(fileName) def handleMouseClick(self,pos): #print 'handleMouseClick',self.verticalScrollBar().value() text_cursor = self.viewer.cursorForPosition(pos) text_position = text_cursor.position() text_cursor.select(QtGui.QTextCursor.LineUnderCursor) line = str(text_cursor.selectedText()) self.emit(QtCore.SIGNAL('lineClicked'),line) word = self.getWord(text_position) self.emit(QtCore.SIGNAL('wordClicked'),word) def handleMouseDoubleClick(self,pos): text_cursor = self.viewer.cursorForPosition(pos) text_position = text_cursor.position() text_cursor.select(QtGui.QTextCursor.LineUnderCursor) line = str(text_cursor.selectedText()) self.emit(QtCore.SIGNAL('lineDoubleClicked'),line) word = self.getWord(text_position) self.emit(QtCore.SIGNAL('wordDoubleClicked'),word) def getWord(self,position): if not self.document: return '' text = str(self.document.toPlainText()) fpos = position-1 lpos = position while fpos>=0 and not [' ','\n'].count(text[fpos]): fpos = fpos -1 while lpos=0: self.viewer.setTextCursor(cursor) found = True if found: self.viewer.ensureCursorVisible() return found #------------------------------------------------------------------- def highlightText(self): #------------------------------------------------------------------- pass ''' # This finds the instances of the search_text but there is not obvious means # to highlight them if ['Highlight all'].count(mode): search_text = self.findText.text() if not search_text: return self.hit_cursors = [] start_cursor = QtGui.QTextCursor(self.viewer.document()) start_cursor.setPosition(0) found_text = 'foo' while found_text: found = self.viewer.document().find(search_text,start_cursor) found_text = str(found.selectedText()) if found_text: self.hit_cursors.append(found) start_cursor.setPosition(found.position()+len(search_text)) #print 'hit_cursors',found,found_text,len(self.hit_cursors),start_cursor.position() ''' def goToTop(self): # This did not work #self.viewer.verticalScrollBar().triggerAction(QtGui.QScrollBar.SliderToMinimum) self.viewer.moveCursor (QtGui.QTextCursor.Start) self.viewer.ensureCursorVisible() ; #print 'goToTop',self.viewer.verticalScrollBar().value(),self.viewer.verticalScrollBar().minimum(),self.viewer.verticalScrollBar().maximum() #------------------------------------------------------------------- def open(self,fileName=None,lineLimit=None,**kw): #------------------------------------------------------------------- import os try: f = open(fileName,'r') except: raise CException(self.__class__,1,fileName) if lineLimit is None or lineLimit is NotImplemented: lineLimit = PREFERENCES().TEXT_VIEW_LINE_LIMIT if lineLimit is not None and lineLimit is not NotImplemented: #print 'reading limited lines',lineLimit text = '' ii = 1 latest = f.readline() while ii < lineLimit and latest != '': text = text + latest latest = f.readline() ii += 1 if ii>=lineLimit: text = text + '\nDISPLAY LIMITED TO '+str( lineLimit)+' LINES\nThe limit can be changed in Preferences\n' else: try: text = f.read() f.close() except: raise CException(self.__class__,2,fileName) CCP4AbstractViewer.CAbstractViewer.open(self,fileName) self.fixedWidthFont = kw.get('fixedWidthFont',None) format = MIMETYPESHANDLER().formatFromFileExt(fileName=fileName) if self.fixedWidthFont is None: if format is not None: self.fixedWidthFont = MIMETYPESHANDLER().getMimeTypeInfo(format,'fixedWidthFont') else: self.fixedWidthFont = False print 'CTextViewer.open',self.fileName,format,self.fixedWidthFont rv = self.loadText(text) self.watchFile() self.goToTop() return rv def reload(self): scrollPosition = self.viewer.verticalScrollBar().value() self.viewer.clear() try: f = open(self.fileName,'r') except: raise CException(self.__class__,1,'filename:'+str(self.fileName)) try: text = f.read() f.close() except: raise CException(self.__class__,2,'filename:'+str(self.fileName)) rv = self.loadText(text) self.viewer.verticalScrollBar().setValue(scrollPosition) return rv #------------------------------------------------------------------- def loadText(self,text,html=0,fixedWidthFont=None): #------------------------------------------------------------------- if self.document is None: self.document=QtGui.QTextDocument(self.viewer) self.viewer.setDocument(self.document) try: if html: self.document.setHtml(text) else: self.document.setPlainText(text) except: raise CException(self.__class__,101,'filename:'+str(self.fileName)) if fixedWidthFont is not None: self.fixedWidthFont = fixedWidthFont if self.fixedWidthFont: self.setFont(style='fixed_width') return 0 def Print(self,painter): text = '' try: f = open(self.fileName,'r') text = f.read() f.close() except: return 1 painter.drawText(0,0,text) #------------------------------------------------------------------- def Save(self,fileName): #------------------------------------------------------------------- #print 'mgTextViewer.Save',self,self.filename,filename import CCP4Utils CCP4Utils.saveFile(fileName,str(self.document.toPlainText())) #------------------------------------------------------------------- def isPrintable(self): #------------------------------------------------------------------- return 1 #------------------------------------------------------------------- def isSaveable(self): #------------------------------------------------------------------- return 1 #------------------------------------------------------------------- def isSearchable(self): #------------------------------------------------------------------- return 1 #------------------------------------------------------------------- def setFont(self,font=None,style=''): #------------------------------------------------------------------- #print 'TextViewer.setFont',self.document,style=='fixed_width' if self.document is None: return if font is not None: self.document.setDefaultFont(font) elif style=='fixed_width': self.viewer.setStyleSheet("QTextBrowser { font-family: Courier ;}") else: self.viewer.setStyleSheet("") return 0 #------------------------------------------------------------------- #------------------------------------------------------------------- #------------------------------------------------------------------- class CCoordsViewer(CTextViewer): #------------------------------------------------------------------- #------------------------------------------------------------------- #------------------------------------------------------------------- MENUTEXT='Display as text' def __init__(self,parent,fileName=''): CTextViewer.__init__(self,parent) if fileName: self.open(fileName) #------------------------------------------------------------------- def loadText(self,text,html=0): #------------------------------------------------------------------- CTextViewer.loadText(self,text=text,html=html) self.setFont(style='fixed_width') ''' #------------------------------------------------------------------- def open(self,filename): #------------------------------------------------------------------- # This method only needed if want ot mark up thr PDB file status,text = self.markupPDB(filename) if not status: self.filename = os.path.abspath(filename) self.setObjectName(os.path.split(self.filename)[-1]) return self.loadText(text,html=1) else: return status font = QtGui.QFont() font.setFixedPitch(1) self.viewer.setCurrentFont(font) #------------------------------------------------------------------- def markupPDB(self,filename): #------------------------------------------------------------------- # Add markup to a PDB file - very slow!! output = '' n = 0 try: f = open(filename) lines = f.readlines() f.close() except: return [1,'Error reading file'] try: for line in lines: if line[0:4] == 'ATOM': n = n + 1 #output = output + '
'+line.strip('\n')+'\n' output = output + '
' +line.strip('\n') + 'go to\n' else: output = output +'
' + line print 'markupPDB',output except: return [1,'Error marking up file'] output = '\n' + output + '\ni\n' return [0,output] ''' #------------------------------------------------------------------- class CMtzHeaderViewer(CTextViewer): #------------------------------------------------------------------- MENUTEXT='Display as text' def __init__(self,parent,fileName=''): CTextViewer.__init__(self,parent) if fileName: self.open(fileName) def open(self,fileName=None,**kw): self.loadText('Awaiting nicer MTZ dump for '+fileName) import os self.fileName = os.path.abspath(fileName) self.setObjectName(os.path.split(fileName)[-1]) class CScriptViewerLogger(QtCore.QObject): def __init__(self,parent): QtCore.QObject.__init__(self,parent) self.log = [] def write(self, data): #import sys #print>>sys.__stdout__, 'logging',data self.emit(QtCore.SIGNAL('newLines'),[data]) self.log.append(data) class CScriptViewer(CTextViewer): def __init__(self,parent,fileName=None): import CCP4Widgets CTextViewer.__init__(self,parent) self.layout().insertWidget(0,CCP4Widgets.CItalicLabel('Enter script:')) self.viewer.setReadOnly(False) self.stdoutViewer = QtGui.QTextEdit(self) self.stdoutViewer.setReadOnly(True) self.layout().addWidget(CCP4Widgets.CItalicLabel('Standard output and errors:')) self.layout().addWidget(self.stdoutViewer) if fileName is not None: self.open(fileName) def open(self,fileName=None,**kw): if fileName is not None: CTextViewer.open(self,fileName=fileName) def isRunable(self): return 1 def run(self): script = self.viewer.toPlainText().__str__() #print '*****CScriptViewer.run',script if len(script)>0: import sys,traceback logger = CScriptViewerLogger(self) self.connect(logger,QtCore.SIGNAL('newLines'),self.appendStdoutViewer) sys.stdout = logger sys.stderr = logger try: exec script except Exception as e: self.appendStdoutViewer(e.__str__().split('\n')) exc_type,exc_value,exc_tb = sys.exc_info()[:3] self.appendStdoutViewer(traceback.format_tb(exc_tb)) sys.stdout = sys.__stdout__ sys.stderr = sys.__stderr__ #self.appendStdoutViewer(logger) def appendStdoutViewer(self,lines): #print 'appendStdoutViewer',logger.log if len(lines)<=0: return self.stdoutViewer.setReadOnly(False) text = self.stdoutViewer.toPlainText().__str__() if len(text)>0 and text[-1]!='\n': text = text + '\n' for line in lines: if line != '\n': text = text + line + '\n' self.stdoutViewer.setPlainText(text) self.stdoutViewer.setReadOnly(True)