--- a/UI/UserInterface.py Sat Dec 01 11:45:24 2018 +0100 +++ b/UI/UserInterface.py Thu Jan 10 14:22:59 2019 +0100 @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2002 - 2018 Detlev Offenbach <detlev@die-offenbachs.de> +# Copyright (c) 2002 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> # """ @@ -19,7 +19,7 @@ from PyQt5.QtCore import pyqtSlot, QTimer, QFile, QFileInfo, pyqtSignal, \ PYQT_VERSION_STR, QDate, QIODevice, qVersion, QProcess, QSize, QUrl, \ - QObject, Qt + QObject, Qt, QUuid, QThread from PyQt5.QtGui import QKeySequence, QDesktopServices from PyQt5.QtWidgets import QSizePolicy, QWidget, QWhatsThis, QToolBar, \ QDialog, QSplitter, QApplication, QMenu, QVBoxLayout, QDockWidget, \ @@ -28,21 +28,6 @@ from PyQt5.QtNetwork import QNetworkProxyFactory, QNetworkAccessManager, \ QNetworkRequest, QNetworkReply -from Globals import qVersionTuple -try: - from PyQt5 import QtWebKit # __IGNORE_WARNING__ - WEBKIT_AVAILABLE = True -except ImportError: - WEBKIT_AVAILABLE = False -if qVersionTuple() < (5, 6, 0): - WEBENGINE_AVAILABLE = False -else: - try: - from PyQt5 import QtWebEngineWidgets # __IGNORE_WARNING__ - WEBENGINE_AVAILABLE = True - except ImportError: - WEBENGINE_AVAILABLE = False - from .Info import Version, VersionOnly, BugAddress, Program, FeatureAddress from . import Config @@ -72,6 +57,8 @@ from eric6config import getConfig +from Globals import qVersionTuple + class Redirector(QObject): """ @@ -282,6 +269,10 @@ self.templateViewer = None self.numbersViewer = None + self.__webBrowserProcess = None + self.__webBrowserClient = None + self.__webBrowserSAName = QUuid.createUuid().toString()[1:-1] + # Create the main window now so that we can connect QActions to it. logging.debug("Creating Layout...") self.__createLayout(debugServer) @@ -571,22 +562,9 @@ self.toolProcs = [] self.__initExternalToolsActions() - # create a dummy help window for shortcuts handling - if WEBENGINE_AVAILABLE: - from WebBrowser.WebBrowserWindow import WebBrowserWindow - self.dummyHelpViewer = \ - WebBrowserWindow(None, '.', None, 'web_browser', True, True) - elif WEBKIT_AVAILABLE: - from Helpviewer.HelpWindow import HelpWindow - self.dummyHelpViewer = \ - HelpWindow(None, '.', None, 'help viewer', True, True) - else: - self.dummyHelpViewer = None - # redirect handling of http and https URLs to ourselves - if WEBENGINE_AVAILABLE or WEBKIT_AVAILABLE: - QDesktopServices.setUrlHandler("http", self.handleUrl) - QDesktopServices.setUrlHandler("https", self.handleUrl) + QDesktopServices.setUrlHandler("http", self.handleUrl) + QDesktopServices.setUrlHandler("https", self.handleUrl) # register all relevant objects splash.showMessage(self.tr("Registering Objects...")) @@ -602,8 +580,6 @@ if self.templateViewer is not None: e5App().registerObject("TemplateViewer", self.templateViewer) e5App().registerObject("Shell", self.shell) - if self.dummyHelpViewer is not None: - e5App().registerObject("DummyHelpViewer", self.dummyHelpViewer) e5App().registerObject("PluginManager", self.pluginManager) e5App().registerObject("ToolbarManager", self.toolbarManager) if self.cooperation is not None: @@ -720,9 +696,6 @@ self.__networkManager.sslErrors.connect(self.__sslErrors) self.__replies = [] - # attribute for the help window - self.helpWindow = None - # set spellchecker defaults from QScintilla.SpellChecker import SpellChecker SpellChecker.setDefaultLanguage( @@ -1816,29 +1789,28 @@ self.whatsThisAct.triggered.connect(self.__whatsThis) self.actions.append(self.whatsThisAct) - if WEBENGINE_AVAILABLE or WEBKIT_AVAILABLE: - self.helpviewerAct = E5Action( - self.tr('Helpviewer'), - UI.PixmapCache.getIcon("help.png"), - self.tr('&Helpviewer...'), - QKeySequence(self.tr("F1")), - 0, self, 'helpviewer') - self.helpviewerAct.setStatusTip(self.tr( - 'Open the helpviewer window')) - self.helpviewerAct.setWhatsThis(self.tr( - """<b>Helpviewer</b>""" - """<p>Display the eric6 web browser. This window will show""" - """ HTML help files and help from Qt help collections. It""" - """ has the capability to navigate to links, set bookmarks,""" - """ print the displayed help and some more features. You may""" - """ use it to browse the internet as well</p><p>If called""" - """ with a word selected, this word is search in the Qt help""" - """ collection.</p>""" - )) - self.helpviewerAct.triggered.connect(self.__helpViewer) - self.actions.append(self.helpviewerAct) - else: - self.helpviewerAct = None + self.helpviewerAct = E5Action( + self.tr('Helpviewer'), + UI.PixmapCache.getIcon("help.png"), + self.tr('&Helpviewer...'), + QKeySequence(self.tr("F1")), + 0, self, 'helpviewer') + self.helpviewerAct.setStatusTip(self.tr( + 'Open the helpviewer window')) + self.helpviewerAct.setWhatsThis(self.tr( + """<b>Helpviewer</b>""" + """<p>Display the eric6 web browser. This window will show""" + """ HTML help files and help from Qt help collections. It""" + """ has the capability to navigate to links, set bookmarks,""" + """ print the displayed help and some more features. You may""" + """ use it to browse the internet as well</p><p>If called""" + """ with a word selected, this word is search in the Qt help""" + """ collection.</p>""" + )) + self.helpviewerAct.triggered.connect(self.__helpViewer) + self.actions.append(self.helpviewerAct) +## else: +## self.helpviewerAct = None self.__initQtDocActions() self.__initPythonDocActions() @@ -2145,22 +2117,21 @@ self.hexEditorAct.triggered.connect(self.__openHexEditor) self.actions.append(self.hexEditorAct) - if WEBENGINE_AVAILABLE or WEBKIT_AVAILABLE: - self.webBrowserAct = E5Action( - self.tr('eric6 Web Browser'), - UI.PixmapCache.getIcon("ericWeb.png"), - self.tr('eric6 &Web Browser...'), - 0, 0, self, 'web_browser') - self.webBrowserAct.setStatusTip(self.tr( - 'Start the eric6 Web Browser')) - self.webBrowserAct.setWhatsThis(self.tr( - """<b>eric6 Web Browser</b>""" - """<p>Browse the Internet with the eric6 Web Browser.</p>""" - )) - self.webBrowserAct.triggered.connect(self.__startWebBrowser) - self.actions.append(self.webBrowserAct) - else: - self.webBrowserAct = None + self.webBrowserAct = E5Action( + self.tr('eric6 Web Browser'), + UI.PixmapCache.getIcon("ericWeb.png"), + self.tr('eric6 &Web Browser...'), + 0, 0, self, 'web_browser') + self.webBrowserAct.setStatusTip(self.tr( + 'Start the eric6 Web Browser')) + self.webBrowserAct.setWhatsThis(self.tr( + """<b>eric6 Web Browser</b>""" + """<p>Browse the Internet with the eric6 Web Browser.</p>""" + )) + self.webBrowserAct.triggered.connect(self.__startWebBrowser) + self.actions.append(self.webBrowserAct) +## else: +## self.webBrowserAct = None self.iconEditorAct = E5Action( self.tr('Icon Editor'), @@ -3366,16 +3337,20 @@ .format(sip_version_str) versionText += """<tr><td><b>QScintilla</b></td><td>{0}</td></tr>"""\ .format(QSCINTILLA_VERSION_STR) - if WEBENGINE_AVAILABLE: + try: from WebBrowser.Tools import WebBrowserTools chromeVersion = WebBrowserTools.getWebEngineVersions()[0] versionText += \ """<tr><td><b>WebEngine</b></td><td>{0}</td></tr>"""\ .format(chromeVersion) - if WEBKIT_AVAILABLE: + except ImportError: + pass + try: from PyQt5.QtWebKit import qWebKitVersion versionText += """<tr><td><b>WebKit</b></td><td>{0}</td></tr>"""\ .format(qWebKitVersion()) + except ImportError: + pass versionText += """<tr><td><b>{0}</b></td><td>{1}</td></tr>"""\ .format(Program, Version) versionText += self.tr("""</table>""") @@ -5234,10 +5209,7 @@ if home.endswith(".chm"): self.__chmViewer(home) else: - if WEBENGINE_AVAILABLE: - hvType = Preferences.getWebBrowser("HelpViewerType") - else: - hvType = Preferences.getHelp("HelpViewerType") + hvType = Preferences.getWebBrowser("HelpViewerType") if hvType == 1: self.launchHelpViewer(home) elif hvType == 2: @@ -5298,10 +5270,7 @@ if home.endswith(".chm"): self.__chmViewer(home) else: - if WEBENGINE_AVAILABLE: - hvType = Preferences.getWebBrowser("HelpViewerType") - else: - hvType = Preferences.getHelp("HelpViewerType") + hvType = Preferences.getWebBrowser("HelpViewerType") if hvType == 1: self.launchHelpViewer(home) elif hvType == 2: @@ -5367,10 +5336,7 @@ else: home = "file://" + home - if WEBENGINE_AVAILABLE: - hvType = Preferences.getWebBrowser("HelpViewerType") - else: - hvType = Preferences.getHelp("HelpViewerType") + hvType = Preferences.getWebBrowser("HelpViewerType") if hvType == 1: self.launchHelpViewer(home) elif hvType == 2: @@ -5432,10 +5398,7 @@ else: home = pyqt4DocDir - if WEBENGINE_AVAILABLE: - hvType = Preferences.getWebBrowser("HelpViewerType") - else: - hvType = Preferences.getHelp("HelpViewerType") + hvType = Preferences.getWebBrowser("HelpViewerType") if hvType == 1: self.launchHelpViewer(home) elif hvType == 2: @@ -5499,10 +5462,7 @@ else: home = pyqt5DocDir - if WEBENGINE_AVAILABLE: - hvType = Preferences.getWebBrowser("HelpViewerType") - else: - hvType = Preferences.getHelp("HelpViewerType") + hvType = Preferences.getWebBrowser("HelpViewerType") if hvType == 1: self.launchHelpViewer(home) elif hvType == 2: @@ -5539,10 +5499,7 @@ else: home = "file://" + home - if WEBENGINE_AVAILABLE: - hvType = Preferences.getWebBrowser("HelpViewerType") - else: - hvType = Preferences.getHelp("HelpViewerType") + hvType = Preferences.getWebBrowser("HelpViewerType") if hvType == 1: self.launchHelpViewer(home) elif hvType == 2: @@ -5608,10 +5565,7 @@ else: home = pysideDocDir - if WEBENGINE_AVAILABLE: - hvType = Preferences.getWebBrowser("HelpViewerType") - else: - hvType = Preferences.getHelp("HelpViewerType") + hvType = Preferences.getWebBrowser("HelpViewerType") if hvType == 1: self.launchHelpViewer(home) elif hvType == 2: @@ -5653,63 +5607,133 @@ if not homeUrl.scheme(): home = QUrl.fromLocalFile(home).toString() - if WEBENGINE_AVAILABLE or WEBKIT_AVAILABLE: - single = useSingle - if WEBENGINE_AVAILABLE: - single = single or \ - Preferences.getWebBrowser("SingleWebBrowserWindow") - elif WEBKIT_AVAILABLE: - single = single or Preferences.getHelp("SingleHelpWindow") - if not single or self.helpWindow is None: - if WEBENGINE_AVAILABLE: - from WebBrowser.WebBrowserWindow import WebBrowserWindow - browser = WebBrowserWindow(home, '.', None, 'web_browser', - True, searchWord=searchWord) - elif WEBKIT_AVAILABLE: - from Helpviewer.HelpWindow import HelpWindow - browser = HelpWindow(home, '.', None, 'help viewer', True, - searchWord=searchWord) - - if QApplication.desktop().width() > 400 and \ - QApplication.desktop().height() > 500: - browser.show() - else: - browser.showMaximized() - - if single: - self.helpWindow = browser - try: - self.helpWindow.webBrowserWindowClosed.connect( - self.__helpClosed) - except AttributeError: - self.helpWindow.helpClosed.connect(self.__helpClosed) - self.preferencesChanged.connect( - self.helpWindow.preferencesChanged) - self.masterPasswordChanged.connect( - self.helpWindow.masterPasswordChanged) - elif searchWord is not None: - self.helpWindow.search(searchWord) - self.helpWindow.raise_() - else: - self.helpWindow.newTab(home) - self.helpWindow.raise_() - else: + launchResult = self.__launchExternalWebBrowser( + home, searchWord=searchWord) + if not launchResult: self.__webBrowser(home) - def __helpClosed(self): - """ - Private slot to handle the helpClosed signal of the help window. - """ - if WEBENGINE_AVAILABLE: - single = Preferences.getWebBrowser("SingleWebBrowserWindow") - elif WEBKIT_AVAILABLE: - single = Preferences.getHelp("SingleHelpWindow") - if single: - self.preferencesChanged.disconnect( - self.helpWindow.preferencesChanged) - self.masterPasswordChanged.disconnect( - self.helpWindow.masterPasswordChanged) - self.helpWindow = None + def __launchExternalWebBrowser(self, home, searchWord=None): + """ + Private method to start an external web browser and communicate with + it. + + @param home filename of file to be shown or URL to be opened + @type str + @keyparam searchWord word to search for + @type str + @return flag indicating a successful launch + @rtype bool + """ + clientArgs = [] + if searchWord: + clientArgs.append("--search={0}".format(searchWord)) + + if self.__webBrowserProcess is None: + webBrowsers = [ + os.path.join( + os.path.dirname(__file__), "..", "eric6_browser.py"), + # QtWebEngine based web browser + os.path.join( + os.path.dirname(__file__), "..", "eric6_webbrowser.py"), + # QtWebKit based web browser + ] + process = QProcess() + for browser in webBrowsers: + args = [ + browser, + "--quiet", + "--qthelp", + "--single", + "--name={0}".format(self.__webBrowserSAName) + ] + process.start(sys.executable, args) + if not process.waitForStarted(): + E5MessageBox.warning( + self, + self.tr("Start Web Browser"), + self.tr("""The eric6 web browser could not be""" + """ started.""")) + return False + + res = self.__connectToWebBrowser(process) + if res == 1: + # connection unsuccessful + return False + elif res == 0: + # successful + break + else: + return False + + process.finished.connect(self.__webBrowserFinished) + self.__webBrowserProcess = process + + if home: + clientArgs.append(home) + else: + clientArgs.append("--newtab={0}".format(home)) + + if clientArgs: + self.__webBrowserClient.processArgs(clientArgs, disconnect=False) + + return True + + def __connectToWebBrowser(self, process): + """ + Private method to connect to a started web browser. + + @param process reference to the started web browser process + @type QProcess + @return error indication (1 = connection not possible, 0 = ok, + -1 = server exited with an error code) + @rtype int + """ + from WebBrowser.WebBrowserSingleApplication import \ + WebBrowserSingleApplicationClient + + webBrowserClient = WebBrowserSingleApplicationClient( + self.__webBrowserSAName) + connectCount = 30 + while connectCount: + res = webBrowserClient.connect() + if res != 0: + break + else: + connectCount -= 1 + QThread.msleep(1000) + QApplication.processEvents() + if process.state() == QProcess.NotRunning and \ + process.exitStatus() == QProcess.NormalExit and \ + process.exitCode() == 100: + # Process exited prematurely due to missing pre-requisites + return -1 + if res <= 0: + E5MessageBox.warning( + self, + self.tr("Start Web Browser"), + self.tr("""<p>The eric6 web browser is not started.</p>""" + """<p>Reason: {0}</p>""").format( + webBrowserClient.errstr()) + ) + return 1 + + self.__webBrowserClient = webBrowserClient + return 0 + + def __webBrowserFinished(self): + """ + Private slot handling the end of the external web browser process. + """ + self.__webBrowserProcess.deleteLater() + + self.__webBrowserProcess = None + self.__webBrowserClient = None + + def __webBrowserShutdown(self): + """ + Private method to shut down the web browser. + """ + self.__webBrowserClient.processArgs(["--shutdown"], disconnect=False) def __helpViewer(self): """ @@ -5734,22 +5758,6 @@ self.tr('Open Browser'), self.tr('Could not start a web browser')) - def getHelpViewer(self, preview=False): - """ - Public method to get a reference to the help window instance. - - @keyparam preview flag indicating to get a help window for preview - (boolean) - @return reference to the help window instance (HelpWindow) - """ - if WEBENGINE_AVAILABLE or WEBKIT_AVAILABLE: - if self.helpWindow is None: - self.launchHelpViewer("", useSingle=True) - self.helpWindow.raise_() - return self.helpWindow - else: - return None - @pyqtSlot() @pyqtSlot(str) def showPreferences(self, pageName=None): @@ -5762,7 +5770,6 @@ dlg = ConfigurationDialog( self, 'Configuration', expandedEntries=self.__expandedConfigurationEntries, - webEngine=WEBENGINE_AVAILABLE, ) dlg.preferencesChanged.connect(self.__preferencesChanged) dlg.masterPasswordChanged.connect(self.__masterPasswordChanged) @@ -5872,19 +5879,21 @@ @param oldPassword current master password (string) @param newPassword new master password (string) """ + import Globals + self.masterPasswordChanged.emit(oldPassword, newPassword) Preferences.convertPasswords(oldPassword, newPassword) - if self.helpWindow is None: - if WEBENGINE_AVAILABLE: - from WebBrowser.Passwords.PasswordManager import \ - PasswordManager - pwManager = PasswordManager() - pwManager.masterPasswordChanged(oldPassword, newPassword) - elif WEBKIT_AVAILABLE: - from Helpviewer.Passwords.PasswordManager import \ - PasswordManager - pwManager = PasswordManager() - pwManager.masterPasswordChanged(oldPassword, newPassword) + variant = Globals.getWebBrowserSupport() + if variant == "QtWebEngine": + from WebBrowser.Passwords.PasswordManager import \ + PasswordManager + pwManager = PasswordManager() + pwManager.masterPasswordChanged(oldPassword, newPassword) + elif variant == "QtWebKit": + from Helpviewer.Passwords.PasswordManager import \ + PasswordManager + pwManager = PasswordManager() + pwManager.masterPasswordChanged(oldPassword, newPassword) Utilities.crypto.changeRememberedMaster(newPassword) def __reloadAPIs(self): @@ -5936,7 +5945,7 @@ """ if self.shortcutsDialog is None: from Preferences.ShortcutsDialog import ShortcutsDialog - self.shortcutsDialog = ShortcutsDialog(self, 'Shortcuts') + self.shortcutsDialog = ShortcutsDialog(self) self.shortcutsDialog.populate() self.shortcutsDialog.show() @@ -6563,9 +6572,8 @@ if self.shutdownCalled: return True - if self.helpWindow is not None: - if not self.helpWindow.shutdown(): - return False + if self.__webBrowserProcess is not None: + self.__webBrowserShutdown() if self.irc is not None: if not self.irc.shutdown():