""" CCP4WebView.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 - Copied from CCP4mg """ ##@package CCP4WebView (QtWebKit) Web browser 'plugin' to view web pages import os import re from PyQt4 import QtGui,QtCore,QtWebKit from core.CCP4ErrorHandling import * from core import CCP4Modules,CCP4Config def setGlobalSettings(): webSettings = QtWebKit.QWebSettings.globalSettings() #print 'setGlobalSetting webSettings',webSettings,key,value preferences = CCP4Modules.PREFERENCES() webSettings.setFontSize(QtWebKit.QWebSettings.DefaultFontSize,preferences.REPORT_FONT_SIZE) webSettings.setFontSize(QtWebKit.QWebSettings.DefaultFixedFontSize,preferences.REPORT_FONT_SIZE) webSettings.setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled,True) for window in CWebView.Instances: window.setLoggraphFont() def isAlive(qobj): import sip try: sip.unwrapinstance(qobj) except RuntimeError: return False return True class CWebPage(QtWebKit.QWebPage): def __init__(self,parent=None): QtWebKit.QWebPage.__init__(self,parent) #self.setLinkDelegationPolicy(QtWebKit.QWebPage.DelegateAllLinks) self.setForwardUnsupportedContent(True) self.settings().setAttribute(QtWebKit.QWebSettings.JavascriptEnabled,1) self.settings().setAttribute(QtWebKit.QWebSettings.PluginsEnabled,1) def shouldInterruptJavaScript(self): # Reimplemented to prevent accasianally seen message box (by Phil) when creating DR reports # Implied from http://forum.qt.io/topic/21387/maximum-duration-for-a-script/3 #http://stackoverflow.com/questions/9284511/reimplement-the-shouldinterruptjavascript-in-qt-c print 'CWebPage.shouldInterruptJavaScript called' import traceback traceback.print_stack() #QApplication::processEvents(QEventLoop::AllEvents, 42) CCP4Modules.QTAPPLICATION().processEvents(QtCore.QEventLoop.AllEvents,42) return False def javaScriptConsoleMessage ( self, message, lineNumber, sourceID ) : return ''' print " ############ JAVASCRIPT MESSAGE ########### " print "lineNumber: ", lineNumber, " sourceID: ", sourceID, " message: ", message print " ########################################### " ''' def acceptNavigationRequest(self,frame=None,request=None,type=None): from core import CCP4Modules #print 'CWebPage.acceptNavigationRequest',request,request.url().path(),request.url().isLocalFile() # if CCP4Modules.PREFERENCES().EXTERNAL_FILES_IN_EXTERNAL_BROWSER and not request.url().isLocalFile(): isXiaReport = re.search('xia2.html', str(request.url().path())) is not None if (CCP4Modules.PREFERENCES().EXTERNAL_FILES_IN_EXTERNAL_BROWSER and not request.url().isLocalFile()) or isXiaReport: rv = QtGui.QDesktopServices.openUrl(request.url()) if not rv: QtGui.QMessageBox.warning(None,'Display url','Attempting to display external URL using non-CCP4i2 web browser failed') return False path = str(request.url().path()) #print 'acceptNavigationRequest path',path format = CCP4Modules.MIMETYPESHANDLER().formatFromFileExt(path) #print 'CWebPage.acceptNavigationRequest',request.url().path(),format if format and not CCP4Modules.MIMETYPESHANDLER().useWebBrowser(format): self.emit(QtCore.SIGNAL("CustomMimeTypeRequested"),request.url()) return False # CCP4WebBrowser is not taking responsibility if self.parent().blockLoading(): self.emit(QtCore.SIGNAL("NavigationRequest"),request.url()) return False # This is broken if setHtml() has been used to set the page text and give # a local base href - the base href is returned by request.url() #self.view().extractCCP4Data(request.url()) return True def clickOn(self,pos): #print 'CWebpage.clickOn', pos.x(),pos.y() # send a mouse click event to the web page event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress, pos, QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, QtCore.Qt.NoModifier) CCP4Modules.QTAPPLICATION().sendEvent(self, event) event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonRelease, pos, QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, QtCore.Qt.NoModifier) CCP4Modules.QTAPPLICATION().sendEvent(self, event) class CWebView(QtWebKit.QWebView): ERROR_CODES= { 101 : { 'description' : 'Error creating CCP4i2 Web Plugins' } } Instances = set() def __init__(self,parent=None,blockLoading=False): QtWebKit.QWebView.__init__(self,parent) CWebView.Instances.add(self) self.connect(self,QtCore.SIGNAL("destroyed(QObject*)"),CWebView.updateInstances) self._blockLoading = blockLoading self.fileName = None self.report = None self.resetScroll = None self.mgpage = CWebPage(self) self.customContextMenu = None self.setPage(self.mgpage) self.connect(self.mgpage,QtCore.SIGNAL("CustomMimeTypeRequested"),self.CustomMimeTypeRequested) self.connect(self.mgpage,QtCore.SIGNAL("statusBarMessage(const QString&)"),self.StatusBarMessage) self.connect(self.mgpage,QtCore.SIGNAL("linkHovered(const QString &, const QString &, const QString &)"),self.LinkHovered) self.connect(self,QtCore.SIGNAL("linkClicked(const QUrl &)"),self.linkClicked) self.connect(self,QtCore.SIGNAL("loadFinished(bool)"),self.LoadFinished) self.connect(self,QtCore.SIGNAL("loadStarted()"),self.LoadStarted) shortcut = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Ctrl+C", "Copy")),self) self.connect(shortcut,QtCore.SIGNAL("activated()"),self.copyHighlighted) """ #So we need a preferences pane for browser.... self.settings().setFontSize(QtWebKit.QWebSettings.MinimumFontSize,9) self.settings().setFontSize(QtWebKit.QWebSettings.DefaultFontSize,16) self.settings().setFontSize(QtWebKit.QWebSettings.DefaultFixedFontSize,18) self.settings().setFontFamily(QtWebKit.QWebSettings.StandardFont,"Times") """ self.setTarget('') self.connect(self.mgpage,QtCore.SIGNAL("unsupportedContent(QNetworkReply*)"),self.handleUnsupportedContent) #Create Qt web plugin factory try: from qtgui import CCP4WebPluginFactory factory = CCP4WebPluginFactory.CWebPluginFactory(self) self.page().setPluginFactory(factory) except CException as e: print 'CCP4WebView Error creating web plugin factory' except Exception as e: print 'CCP4WebView Error creating web plugin factory' ''' def sizeHint(self): return QtCore.QSize(400,600) ''' @staticmethod def updateInstances(qobj): CWebView.Instances = set([window for window in CWebView.Instances if isAlive(window)]) def objectPath(self): if self.mgpage is None: return '' else: return str(self.mgpage.objectName()) def blockLoading(self): return self._blockLoading def createWindow(self,type=None): view = CWebView() self.emit(QtCore.SIGNAL("NewWindowRequested"),view) return view def LoadStarted(self,): icon = QtGui.QIcon() self.emit(QtCore.SIGNAL("IconReady"),(icon,self)) def setTarget(self,target=None): self.target=target def LoadFinished(self,ok=None): icon = self.icon() #print "CWebView.LoadFinished", self.target,ok,self.url().toLocalFile() if ok: return if self.target: #print 'currentFrame',self.page().mainFrame(),self.target.strip('#') self.page().mainFrame().scrollToAnchor(self.target.strip('#')) self.setTarget('') #self.load(myurl) self.emit(QtCore.SIGNAL("IconReady"),(icon,self)) self.subJobReport = CSubJobReport(self) self.page().currentFrame().addToJavaScriptWindowObject('SubJobReport',self.subJobReport) self.setLoggraphFont() if self.resetScroll is not None: self.page().mainFrame().setScrollBarValue(QtCore.Qt.Vertical,self.resetScroll) self.resetScroll = None def contextMenuEvent(self,event): #print 'CWebView.contextMenuEvent',event.pos().x(),event.pos().y() hitTestResult = None alt_words = [] frame = self.page().frameAt(event.pos()) if frame: hitTestResult = frame.hitTestContent(event.pos()) #print 'CWebView.contextMenuEvent',frame,hitTestResult #print 'CWebView.contextMenuEvent hitTestResult alt text',str(hitTestResult.alternateText()) alt_text = hitTestResult.alternateText() if not alt_text.isEmpty(): alt_words = (str(alt_text)).split() #print 'CWebView.contextMenuEvent alt text',alt_words if hitTestResult: webElement = hitTestResult.element() if len(alt_words)>=2 and alt_words[0] == 'x-ccp4-widget/CDataFileView': if not self.customContextMenu: from qtgui import CCP4GuiUtils self.customContextMenu = QtGui.QMenu(self) CCP4GuiUtils.populateMenu(self,self.customContextMenu,['copy','view','help'],default_icon='') self.customContextMenu.popup(event.globalPos()) event.accept() else: QtWebKit.QWebView.contextMenuEvent(self,event) def getActionDef(self,name=''): if name == 'copy': return dict ( text = self.tr("Copy"), tip = self.tr('Copy data'), #enabled = e, slot = self.dummyHandler ) if name == 'help': return dict ( text = self.tr("Help"), tip = self.tr('Help'), slot = self.dummyHandler ) if name == 'view': return dict ( text = self.tr("View"), tip = self.tr('View data'), slot = self.dummyHandler ) def dummyHandler(self): pass def CustomMimeTypeRequested(self,url=None): if url: self.emit(QtCore.SIGNAL("CustomMimeTypeRequested"),url) def copyHighlighted(self): CCP4Modules.QTAPPLICATION().clipboard().setText(self.selectedText()) def handleUnsupportedContent(self,qnr): #print 'handleUnsupportedContent',qnr,qnr.url(),'scheme',str(qnr.url().scheme()),'path',str(qnr.url().path()) pass def LinkHovered(self,link,title,content): self.emit(QtCore.SIGNAL("StatusBarMessage"),(link)) def linkClicked(self,url): print 'CWebView.linkClicked',url.toLocalFile(),url.fragment() self.load(url) def StatusBarMessage(self,message=None): self.emit(QtCore.SIGNAL("StatusBarMessage"),(message)) def isPrintable(self): return 1 def isSaveable(self): return 1 def isSearchable(self): return 1 def isRunable(self): return 0 def findText(self,subString='',direction=1,caseSensitive=0,wrapAroundDocument=1,highlightAll=0): flags = 0 #if highlightAll: flags = flags | QtWebKit.QWebPage.HighlightAllOccurrences if wrapAroundDocument: flags = flags | QtWebKit.QWebPage.FindWrapsAroundDocument if direction<0: flags = flags | QtWebKit.QWebPage.FindBackward if caseSensitive: flags = flags | QtWebKit.QWebPage.FindCaseSensitively #print 'CWebView.findText',subString,flags if flags: return self.mgpage.findText(QtCore.QString(subString),flags) else: return self.mgpage.findText(QtCore.QString(subString)) def getFileExt(self): from qtcore import CCP4CustomMimeTypes return CCP4CustomMimeTypes.MimeTypesHandler().getMimeTypeInfo(name='text/html',info='fileExtensions') def getLabel(self): from qtcore import CCP4CustomMimeTypes return CCP4CustomMimeTypes.MimeTypesHandler().getMimeTypeInfo(name='text/html',info='description') def Save(self,fileName): from core import CCP4Utils CCP4Utils.saveFile(fileName,str(self.mgpage.currentFrame().toHtml())) def title(self): t = QtWebKit.QWebView.title(self) return str(t) def load(self,url,reportErrors=False): self.extractCCP4Data(url,reportErrors=reportErrors) #print 'WebView.load',str(url.toLocalFile()),self.report,self.report.resetBaseHref self.editHTTPPort(self.fileName) block = self._blockLoading self._blockLoading = False if self.report is None or self.report.resetBaseHref is None: QtWebKit.QWebView.load(self,url) else: url = QtCore.QUrl.fromLocalFile(self.report.resetBaseHref) QtWebKit.QWebView.load(self,url) #print 'CWebView.load fragment',url.fragment() if url.isLocalFile(): self.fileName = str(url.toLocalFile()) else: self.fileName = str(url.toString()) self._blockLoading = block #print 'CWebView.load _blockLoading',self._blockLoading def attemptImageLoad(self): self.page().mainFrame().evaluateJavaScript(''' var imgElements = document.getElementsByTagName('img'); for (var i=0; i1: ext=ext[1:] #print 'CWebView.extractCCP4Data',self.fileName if html_exts.count(ext) and os.path.exists(self.fileName): self.lastModTime = os.path.getmtime(self.fileName) from qtcore import CCP4Report self.report = CCP4Report.CReport() try: self.report.loadFromXmlFile(self.fileName) #print 'CWebView.extractCCP4Data: Loaded CCP4 data:',self.report.containsCCP4Data() except CException as e: if reportErrors: e.warningMessage(windowTitle=self.windowTitle()) else: self.fileName = '' self.lastModTime = None def isFileModified(self): if len(self.fileName) == 0: return False modTime = os.path.getmtime(self.fileName) if modTime > self.lastModTime: return True else: return False def close(self): pass def clear(self): self.mgpage = CWebPage(self) self.setPage(self.mgpage) def handleTabbedOpen(self): pass def handleTabbedClosed(self): pass def setLoggraphFont(self, font={}): #print 'setLoggraphFont',CCP4Modules.PREFERENCES().REPORT_FONT_SIZE-2,font fontSel = { 'family' : 'Lucida Sans Unicode', 'size' : int(CCP4Modules.PREFERENCES().REPORT_FONT_SIZE-2)} fontSel.update(font) from pimple import MGQTmatplotlib loggraphList = self.findChildren(MGQTmatplotlib.LogGraph) #print 'CWebPage.setLoggraphFont',loggraphList for loggraph in loggraphList: loggraph. applyPreferences(titleFontSel = fontSel,axesFontSel= fontSel, axesLabelFontSel=fontSel, legendFontSel = fontSel ) def emitDownloadRequest(self, jobId, dataName): print 'CWebView.emitDownloadRequest', jobId, dataName self.emit(QtCore.SIGNAL('csvDownloadRequest'), jobId, dataName) class CSubJobReport(QtCore.QObject): def __init__(self,parent): QtCore.QObject.__init__(self,parent) @QtCore.pyqtSlot(unicode) def load(self,idText): ''' Invoked from xreport.js to merge sub-job report into paret report ''' reportErr = True if idText[0:5] != 'jobId': print 'ERROR in load sub-job report - CSubJobReport.load did not receive jobId' return jobId = int(idText[5:]) from dbapi import CCP4DbUtils from report import CCP4ReportGenerator openJob = CCP4DbUtils.COpenJob(jobId=jobId) generator = CCP4ReportGenerator.CReportGenerator(jobId=openJob.jobId,jobStatus=openJob.status) #self.connect(generator,QtCore.SIGNAL('FinishedPictures'),self.handleMgFinished) if CCP4Config.DEVELOPER(): reportFile, newPageOrNewData = generator.makeReportFile() try: reportFile, newPageOrNewData = generator.makeReportFile() except CException as e: if reportErr and e.maxSeverity()>SEVERITY_WARNING: e.warningMessage() except Exception as e: if reportErr: QtGui.QMessageBox.warning(self,self.parent().windowTitle(),'Unknown error creating report file for job number '+str(openJob.jobNumber)) #print 'CSubJobReport.',reportFile if os.path.exists(reportFile): err = self.merge(reportFile,idText) def merge(self,reportFile,idText): from lxml import etree from core import CCP4Utils ''' parentTree = etree.parse(parentFile) print 'mergeIntoParent jobId',self.jobId # Find the span-link created by CCP4ReportParser.foldLinkLine path = 'body/div[@class="sub-job-list"]/span[@class="folder_link"]/a[@id="jobId'+str(self.jobId)+'"]' aEle = parentTree.xpath(path) print 'mergeIntoParent aEle',aEle if len(aEle) == 0: return CErrorReport(self.__class__,5,parentFile) aEle = aEle[0] label = aEle.text print 'mergeIntoParent',label insertEle = aEle.xpath('../..')[0] insertIndex = insertEle.index(aEle.xpath('..')[0]) ''' #myTree = etree.parse(reportFile) myTree = CCP4Utils.openFileToEtree(reportFile) # Load the ccp4_data while we have the file as xml self.parent().report.loadFromEtree(myTree) # Convert body of sub-job report html to a div.subjob body = myTree.xpath('./body')[0] body.tag ='div' body.set('id',str(idText)) body.set('class','subjob') subJobText = etree.tostring(body) #print 'CSubJobReport.merge subJobText(cut)',subJobText[0:200] ele = self.parent().page().currentFrame().findFirstElement('body div#'+idText) #print 'CSubJobReport.merge div ele',ele,ele.isNull() if not ele.isNull(): ele.replace(subJobText) ele.setStyleProperty('display','block') #print 'CSubJobReport.merge done replace' #self.parent().reload() #print 'CSubJobReport.merge done reload' text = self.parent().page().currentFrame().toHtml().__str__() from core import CCP4Utils CCP4Utils.saveFile(self.parent().fileName+'.tmp',text,overwrite=True) #self.parent().fileName = self.parent().fileName+'.tmp' #self.parent().reolad()