Continued porting the web browser. QtWebEngine

Mon, 22 Feb 2016 19:57:58 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Mon, 22 Feb 2016 19:57:58 +0100
branch
QtWebEngine
changeset 4766
5f8d08aa2217
parent 4763
8ad353f31184
child 4767
0bace7c5ebc9

Continued porting the web browser.

- continued adding the GreaseMonkey stuff

Preferences/__init__.py file | annotate | diff | comparison | revisions
WebBrowser/GreaseMonkey/GreaseMonkeyManager.py file | annotate | diff | comparison | revisions
WebBrowser/GreaseMonkey/GreaseMonkeyScript.py file | annotate | diff | comparison | revisions
WebBrowser/Network/NetworkManager.py file | annotate | diff | comparison | revisions
WebBrowser/Tools/DelayedFileWatcher.py file | annotate | diff | comparison | revisions
WebBrowser/Tools/Scripts.py file | annotate | diff | comparison | revisions
--- a/Preferences/__init__.py	Sun Feb 21 19:54:14 2016 +0100
+++ b/Preferences/__init__.py	Mon Feb 22 19:57:58 2016 +0100
@@ -1026,6 +1026,8 @@
                                     # search engine name)
         "SearchLanguage": QLocale().language(),
         "RssFeeds": [],
+        # Grease Monkey
+        "GreaseMonkeyDisabledScripts": [],
         # Flash Cookie Manager: identical to helpDefaults
         # PIM:                  identical to helpDefaults
         # VirusTotal:           identical to helpDefaults
@@ -2767,9 +2769,10 @@
 ##                 "ClickToFlashWhitelist", "SendRefererWhitelist",
 ##                 "GreaseMonkeyDisabledScripts", "NoCacheHosts",
 ##                 "FlashCookiesWhitelist", "FlashCookiesBlacklist",
-##                 ]:
-##        return toList(prefClass.settings.value(
-##            "WebBrowser/" + key, prefClass.helpDefaults[key]))
+    elif key in ["GreaseMonkeyDisabledScripts",
+                 ]:
+        return toList(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.helpDefaults[key]))
     else:
         return prefClass.settings.value("WebBrowser/" + key,
                                         prefClass.webBrowserDefaults[key])
--- a/WebBrowser/GreaseMonkey/GreaseMonkeyManager.py	Sun Feb 21 19:54:14 2016 +0100
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyManager.py	Mon Feb 22 19:57:58 2016 +0100
@@ -18,6 +18,9 @@
 import Utilities
 import Preferences
 
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+from .GreaseMonkeyUrlInterceptor import GreaseMonkeyUrlInterceptor
+
 
 class GreaseMonkeyManager(QObject):
     """
@@ -34,22 +37,21 @@
         super(GreaseMonkeyManager, self).__init__(parent)
         
         self.__disabledScripts = []
-        self.__endScripts = []
-        self.__startScripts = []
+        self.__scripts = []
         self.__downloaders = []
         
+        self.__interceptor = GreaseMonkeyUrlInterceptor(self)
+        WebBrowserWindow.networkManager().installUrlInterceptor(
+            self.__interceptor)
+        
         QTimer.singleShot(0, self.__load)
-##    , m_interceptor(new GM_UrlInterceptor(this))
-##{
-##    mApp->networkManager()->installUrlInterceptor(m_interceptor);
-##
-##    QTimer::singleShot(0, this, SLOT(load()));
-##}
-##
-##GM_Manager::~GM_Manager()
-##{
-##    mApp->networkManager()->removeUrlInterceptor(m_interceptor);
-##}
+    
+    def __del__(self):
+        """
+        Special method called during object destruction.
+        """
+        WebBrowserWindow.networkManager().removeUrlInterceptor(
+            self.__interceptor)
     
     def showConfigurationDialog(self, parent=None):
         """
@@ -143,7 +145,7 @@
         
         @return list of all scripts (list of GreaseMonkeyScript)
         """
-        return self.__startScripts[:] + self.__endScripts[:]
+        return self.__scripts[:]
     
     def containsScript(self, fullName):
         """
@@ -152,12 +154,10 @@
         @param fullName full name of the script (string)
         @return flag indicating the existence (boolean)
         """
-        for script in self.__startScripts:
+        for script in self.__scripts:
             if script.fullName() == fullName:
                 return True
-        for script in self.__endScripts:
-            if script.fullName() == fullName:
-                return True
+        
         return False
     
     def enableScript(self, script):
@@ -170,14 +170,9 @@
         fullName = script.fullName()
         if fullName in self.__disabledScripts:
             self.__disabledScripts.remove(fullName)
-##void GM_Manager::enableScript(GM_Script* script)
-##{
-##    script->setEnabled(true);
-##    m_disabledScripts.removeOne(script->fullName());
-##
-##    QWebEngineScriptCollection *collection = mApp->webProfile()->scripts();
-##    collection->insert(script->webScript());
-##}
+        
+        collection = WebBrowserWindow.webProfile().scripts()
+        collection.insert(script.webScript())
     
     def disableScript(self, script):
         """
@@ -189,13 +184,9 @@
         fullName = script.fullName()
         if fullName not in self.__disabledScripts:
             self.__disabledScripts.append(fullName)
-##void GM_Manager::disableScript(GM_Script* script)
-##{
-##    script->setEnabled(false);
-##    m_disabledScripts.append(script->fullName());
-##
-##    QWebEngineScriptCollection *collection = mApp->webProfile()->scripts();
-##    collection->remove(collection->findScript(script->fullName()));
+        
+        collection = WebBrowserWindow.webProfile().scripts()
+        collection.remove(collection.findScript(fullName))
     
     def addScript(self, script):
         """
@@ -204,83 +195,48 @@
         @param script script to be added (GreaseMonkeyScript)
         @return flag indicating success (boolean)
         """
-        if not script:
+        if not script or not script.isValid():
             return False
         
-        from .GreaseMonkeyScript import GreaseMonkeyScript
-        if script.startAt() == GreaseMonkeyScript.DocumentStart:
-            self.__startScripts.append(script)
-        else:
-            self.__endScripts.append(script)
+        self.__scripts.append(script)
+        script.scriptChanged.connect(self.__scriptChanged)
+        
+        collection = WebBrowserWindow.webProfile().scripts()
+        collection.insert(script.webScript())
         
         self.scriptsChanged.emit()
         return True
-##bool GM_Manager::addScript(GM_Script* script)
-##{
-##    if (!script || !script->isValid()) {
-##        return false;
-##    }
-##
-##    m_scripts.append(script);
-##    connect(script, &GM_Script::scriptChanged, this, &GM_Manager::scriptChanged);
-##
-##    QWebEngineScriptCollection *collection = mApp->webProfile()->scripts();
-##    collection->insert(script->webScript());
-##
-##    emit scriptsChanged();
-##    return true;
-##}
     
-    def removeScript(self, script):
+    def removeScript(self, script, removeFile=True):
         """
         Public method to remove a script.
         
         @param script script to be removed (GreaseMonkeyScript)
+        @param removeFile flag indicating to remove the script file as well
+            (bool)
         @return flag indicating success (boolean)
         """
         if not script:
             return False
         
-        from .GreaseMonkeyScript import GreaseMonkeyScript
-        if script.startAt() == GreaseMonkeyScript.DocumentStart:
-            try:
-                self.__startScripts.remove(script)
-            except ValueError:
-                pass
-        else:
-            try:
-                self.__endScripts.remove(script)
-            except ValueError:
-                pass
+        try:
+            self.__scripts.remove(script)
+        except ValueError:
+            pass
         
         fullName = script.fullName()
+        collection = WebBrowserWindow.webProfile().scripts()
+        collection.remove(collection.findScript(fullName))
+        
         if fullName in self.__disabledScripts:
             self.__disabledScripts.remove(fullName)
-        QFile.remove(script.fileName())
+        
+        if removeFile:
+            QFile.remove(script.fileName())
+            del script
         
         self.scriptsChanged.emit()
         return True
-##bool GM_Manager::removeScript(GM_Script* script, bool removeFile)
-##{
-##    if (!script) {
-##        return false;
-##    }
-##
-##    m_scripts.removeOne(script);
-##
-##    QWebEngineScriptCollection *collection = mApp->webProfile()->scripts();
-##    collection->remove(collection->findScript(script->fullName()));
-##
-##    m_disabledScripts.removeOne(script->fullName());
-##
-##    if (removeFile) {
-##        QFile::remove(script->fileName());
-##        delete script;
-##    }
-##
-##    emit scriptsChanged();
-##    return true;
-##}
     
     def canRunOnScheme(self, scheme):
         """
@@ -291,32 +247,6 @@
         """
         return scheme in ["http", "https", "data", "ftp"]
     
-##    def pageLoadStarted(self):
-##        """
-##        Public slot to handle the start of loading a page.
-##        """
-##        frame = self.sender()
-##        if not frame:
-##            return
-##        
-##        urlScheme = frame.url().scheme()
-##        urlString = bytes(frame.url().toEncoded()).decode()
-##        
-##        if not self.canRunOnScheme(urlScheme):
-##            return
-##        
-##        from .GreaseMonkeyJavaScript import bootstrap_js
-##        for script in self.__startScripts:
-##            if script.match(urlString):
-##                frame.evaluateJavaScript(bootstrap_js + script.script())
-##        
-##        for script in self.__endScripts:
-##            if script.match(urlString):
-##                javascript = 'window.addEventListener("DOMContentLoaded",' \
-##                    'function(e) {{ {0} }}, false);'.format(
-##                        bootstrap_js + script.script())
-##                frame.evaluateJavaScript(javascript)
-    
     def __load(self):
         """
         Private slot to load the available scripts into the manager.
@@ -329,101 +259,34 @@
             scriptsDir.mkdir("requires")
         
         self.__disabledScripts = \
-            Preferences.getHelp("GreaseMonkeyDisabledScripts")
+            Preferences.getWebBrowser("GreaseMonkeyDisabledScripts")
         
         from .GreaseMonkeyScript import GreaseMonkeyScript
         for fileName in scriptsDir.entryList(["*.js"], QDir.Files):
             absolutePath = scriptsDir.absoluteFilePath(fileName)
             script = GreaseMonkeyScript(self, absolutePath)
             
+            if not script.isValid():
+                del script
+                continue
+            
+            self.__scripts.append(script)
+            
             if script.fullName() in self.__disabledScripts:
                 script.setEnabled(False)
-            
-            if script.startAt() == GreaseMonkeyScript.DocumentStart:
-                self.__startScripts.append(script)
             else:
-                self.__endScripts.append(script)
-##void GM_Manager::load()
-##{
-##    QDir gmDir(m_settingsPath + QL1S("/greasemonkey"));
-##    if (!gmDir.exists()) {
-##        gmDir.mkdir(m_settingsPath + QL1S("/greasemonkey"));
-##    }
-##
-##    if (!gmDir.exists("requires")) {
-##        gmDir.mkdir("requires");
-##    }
-##
-##    m_bootstrapScript = QzTools::readAllFileContents(":gm/data/bootstrap.min.js");
-##    m_valuesScript = QzTools::readAllFileContents(":gm/data/values.min.js");
-##
-##    QSettings settings(m_settingsPath + QL1S("/extensions.ini"), QSettings::IniFormat);
-##    settings.beginGroup("GreaseMonkey");
-##    m_disabledScripts = settings.value("disabledScripts", QStringList()).toStringList();
-##
-##    foreach (const QString &fileName, gmDir.entryList(QStringList("*.js"), QDir::Files)) {
-##        const QString absolutePath = gmDir.absoluteFilePath(fileName);
-##        GM_Script* script = new GM_Script(this, absolutePath);
-##
-##        if (!script->isValid()) {
-##            delete script;
-##            continue;
-##        }
-##
-##        m_scripts.append(script);
-##
-##        if (m_disabledScripts.contains(script->fullName())) {
-##            script->setEnabled(false);
-##        }
-##        else {
-##            mApp->webProfile()->scripts()->insert(script->webScript());
-##        }
-##    }
-##}
+                collection = WebBrowserWindow.webProfile().scripts()
+                collection.insert(script.webScript())
     
     def __scriptChanged(self):
         """
         Private slot handling a changed script.
         """
-##void GM_Manager::scriptChanged()
-##{
-##    GM_Script *script = qobject_cast<GM_Script*>(sender());
-##    if (!script)
-##        return;
-##
-##    QWebEngineScriptCollection *collection = mApp->webProfile()->scripts();
-##    collection->remove(collection->findScript(script->fullName()));
-##    collection->insert(script->webScript());
-##}
-    
-##    def connectPage(self, page):
-##        """
-##        Public method to allow the GreaseMonkey manager to connect to the page.
-##        
-##        @param page reference to the web page (HelpWebPage)
-##        """
-##        page.mainFrame().javaScriptWindowObjectCleared.connect(
-##            self.pageLoadStarted)
-##    
-##    def createRequest(self, op, request, outgoingData=None):
-##        """
-##        Public method to create a request.
-##        
-##        @param op the operation to be performed
-##            (QNetworkAccessManager.Operation)
-##        @param request reference to the request object (QNetworkRequest)
-##        @param outgoingData reference to an IODevice containing data to be sent
-##            (QIODevice)
-##        @return reference to the created reply object (QNetworkReply)
-##        """
-##        if op == QNetworkAccessManager.GetOperation and \
-##           request.rawHeader(b"X-Eric6-UserLoadAction") == QByteArray(b"1"):
-##            urlString = request.url().toString(
-##                QUrl.RemoveFragment | QUrl.RemoveQuery)
-##            if urlString.endswith(".user.js"):
-##                self.downloadScript(request)
-##                from Helpviewer.Network.EmptyNetworkReply import \
-##                    EmptyNetworkReply
-##                return EmptyNetworkReply(self)
-##        
-##        return None
+        script = self.sender()
+        if not script:
+            return
+        
+        fullName = script.fullName()
+        collection = WebBrowserWindow.webProfile().scripts()
+        collection.remove(collection.findScript(fullName))
+        collection.insert(script.webScript())
--- a/WebBrowser/GreaseMonkey/GreaseMonkeyScript.py	Sun Feb 21 19:54:14 2016 +0100
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyScript.py	Mon Feb 22 19:57:58 2016 +0100
@@ -9,18 +9,25 @@
 
 from __future__ import unicode_literals
 
-from PyQt5.QtCore import QUrl, QRegExp
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl, QRegExp, \
+    QByteArray,  QCryptographicHash
+from PyQt5.QtWebEngineWidgets import QWebEngineScript
 
 from .GreaseMonkeyUrlMatcher import GreaseMonkeyUrlMatcher
+from .GreaseMonkeyJavaScript import bootstrap_js, values_js
+
+from ..Tools.DelayedFileWatcher import DelayedFileWatcher
 
 
-class GreaseMonkeyScript(object):
+class GreaseMonkeyScript(QObject):
     """
     Class implementing the GreaseMonkey script.
     """
     DocumentStart = 0
     DocumentEnd = 1
     
+    scriptChanged = pyqtSignal()
+    
     def __init__(self, manager, path):
         """
         Constructor
@@ -28,7 +35,10 @@
         @param manager reference to the manager object (GreaseMonkeyManager)
         @param path path of the Javascript file (string)
         """
+        super(GreaseMonkeyScript, self).__init__(manager)
+        
         self.__manager = manager
+        self.__fileWatcher = DelayedFileWatcher(parent=None)
         
         self.__name = ""
         self.__namespace = "GreaseMonkeyNS"
@@ -39,6 +49,7 @@
         self.__exclude = []
         
         self.__downloadUrl = QUrl()
+        self.__updateUrl = QUrl()
         self.__startAt = GreaseMonkeyScript.DocumentEnd
         
         self.__script = ""
@@ -46,10 +57,12 @@
         self.__enabled = True
         self.__valid = False
         self.__metaData = ""
+        self.__noFrames = False
         
-        self.__parseScript(path)
-##    , m_fileWatcher(new DelayedFileWatcher(this))
-##    connect(m_fileWatcher, SIGNAL(delayedFileChanged(QString)), this, SLOT(watchedFileChanged(QString)));
+        self.__parseScript()
+        
+        self.__fileWatcher.delayedFileChanged.connect(
+            self.__watchedFileChanged)
     
     def isValid(self):
         """
@@ -107,6 +120,14 @@
         """
         return QUrl(self.__downloadUrl)
     
+    def updateUrl(self):
+        """
+        Public method to get the update URL of the script.
+        
+        @return update URL of the script (QUrl)
+        """
+        return QUrl(self.__updateUrl)
+    
     def startAt(self):
         """
         Public method to get the start point of the script.
@@ -115,13 +136,22 @@
         """
         return self.__startAt
     
+    def noFrames(self):
+        """
+        Public method to get the noFrames flag.
+        
+        @return flag indicating to not run on sub frames
+        @rtype bool
+        """
+        return self.__noFrames
+    
     def isEnabled(self):
         """
         Public method to check, if the script is enabled.
         
         @return flag indicating an enabled state (boolean)
         """
-        return self.__enabled
+        return self.__enabled and self.__valid
     
     def setEnabled(self, enable):
         """
@@ -161,10 +191,14 @@
         """
         return self.__script
     
-##QString GM_Script::metaData() const
-##{
-##    return m_metadata;
-##}
+    def metaData(self):
+        """
+        Public method to get the script meta information.
+        
+        @return script meta information
+        @rtype str
+        """
+        return self.__metaData
     
     def fileName(self):
         """
@@ -181,7 +215,7 @@
         @param urlString URL (string)
         @return flag indicating a match (boolean)
         """
-        if not self.__enabled:
+        if not self.isEnabled():
             return False
         
         for matcher in self.__exclude:
@@ -194,6 +228,22 @@
         
         return False
     
+    @pyqtSlot(str)
+    def __watchedFileChanged(self, fileName):
+        """
+        Private slot handling changes of the script file.
+        
+        @param fileName path of the script file
+        @type str
+        """
+        if self.__fileName == fileName:
+            self.__parseScript()
+            
+            self.__manager.removeScript(self, False)
+            self.__manager.addScript(self)
+            
+            self.scriptChanged.emit()
+    
     def __parseScript(self, path):
         """
         Private method to parse the given script and populate the data
@@ -201,6 +251,24 @@
         
         @param path path of the Javascript file (string)
         """
+        self.__name = ""
+        self.__namespace = "GreaseMonkeyNS"
+        self.__description = ""
+        self.__version = ""
+        
+        self.__include = []
+        self.__exclude = []
+        
+        self.__downloadUrl = QUrl()
+        self.__updateUrl = QUrl()
+        self.__startAt = GreaseMonkeyScript.DocumentEnd
+        
+        self.__script = ""
+        self.__enabled = True
+        self.__valid = False
+        self.__metaData = ""
+        self.__noFrames = False
+        
         try:
             f = open(path, "r", encoding="utf-8")
             fileData = f.read()
@@ -209,6 +277,9 @@
             # silently ignore because it shouldn't happen
             return
         
+        if self.__fileName not in self.__fileWatcher.files():
+            self.__fileWatcher.addPath(self.__fileName)
+        
         rx = QRegExp("// ==UserScript==(.*)// ==/UserScript==")
         rx.indexIn(fileData)
         metaDataBlock = rx.cap(1).strip()
@@ -219,6 +290,9 @@
         
         requireList = []
         for line in metaDataBlock.splitlines():
+            if not line.strip():
+                continue
+            
             if not line.startswith("// @"):
                 continue
             
@@ -247,9 +321,9 @@
             elif key == "@version":
                 self.__version = value
             
-            elif key == "@updateURL":
-                self.__downloadUrl = QUrl(value)
-            
+##            elif key == "@updateURL":
+##                self.__downloadUrl = QUrl(value)
+##            
             elif key in ["@include", "@match"]:
                 self.__include.append(GreaseMonkeyUrlMatcher(value))
             
@@ -267,6 +341,9 @@
             
             elif key == "@downloadURL" and self.__downloadUrl.isEmpty():
                 self.__downloadUrl = QUrl(value)
+            
+            elif key == "@updateURL" and self.__updateUrl.isEmpty():
+                self.__updateUrl = QUrl(value)
         
         if not self.__include:
             self.__include.append(GreaseMonkeyUrlMatcher("*"))
@@ -275,31 +352,32 @@
         index = fileData.find(marker) + len(marker)
         self.__metaData = fileData[:index]
         script = fileData[index:].strip()
-        script = "{0}{1}".format(
-            self.__manager.requireScripts(requireList),
-            script)
-        self.__script = "(function(){{{0}}})();".format(script)
-        self.__valid = len(script) > 0
-##    const QString nspace = QCryptographicHash::hash(fullName().toUtf8(), QCryptographicHash::Md4).toHex();
-##    const QString gmValues = m_manager->valuesScript().arg(nspace);
-##
-##    m_script = QSL("(function(){%1\n%2\n%3\n})();").arg(gmValues, m_manager->requireScripts(requireList), script);
-##    m_valid = true;
+        
+        nspace = bytes(QCryptographicHash.hash(
+            QByteArray(self.fullName().encode("utf-8")),
+            QCryptographicHash.Md4).toHex()).decode("ascii")
+        valuesScript = values_js.format(nspace)
+        self.__script = "(function(){{{0}\n{1}\n{2}\n}})();".format(
+            valuesScript, self.__manager.requireScripts(requireList), script
+        )
+        self.__valid = True
     
     def webScript(self):
         """
-        Public method to create a script object
+        Public method to create a script object.
         
         @return prepared script object
         @rtype QWebEngineScript
         """
-##QWebEngineScript GM_Script::webScript() const
-##{
-##    QWebEngineScript script;
-##    script.setName(fullName());
-##    script.setInjectionPoint(startAt() == DocumentStart ? QWebEngineScript::DocumentCreation : QWebEngineScript::DocumentReady);
-##    script.setWorldId(QWebEngineScript::MainWorld);
-##    script.setRunsOnSubFrames(!m_noframes);
-##    script.setSourceCode(QSL("%1\n%2\n%3").arg(m_metadata, m_manager->bootstrapScript(), m_script));
-##    return script;
-##}
+        script = QWebEngineScript()
+        script.setName(self.fullName())
+        if self.startAt() == GreaseMonkeyScript.DocumentStart:
+            script.setInjectionPoint(QWebEngineScript.DocumentCreation)
+        else:
+            script.setInjectionPoint(QWebEngineScript.DocumentReady)
+        script.setWorldId(QWebEngineScript.MainWorld)
+        script.setRunsOnSubFrames(not self.__noFrames)
+        script.setSourceCode("{0}\n{1}\n{2}".format(
+            self.__metaData, bootstrap_js, self.__script
+        ))
+        return script
--- a/WebBrowser/Network/NetworkManager.py	Sun Feb 21 19:54:14 2016 +0100
+++ b/WebBrowser/Network/NetworkManager.py	Mon Feb 22 19:57:58 2016 +0100
@@ -159,3 +159,11 @@
 ##        from WebBrowser.WebBrowserWindow import WebBrowserWindow
 ##        WebBrowserWindow.webProfile().setHttpAcceptLanguage(
 ##            self.__acceptLanguage)
+    
+    def installUrlInterceptor(self, interceptor):
+        # TODO: Qt 5.6, URL Interceptor
+        pass
+    
+    def removeUrlInterceptor(self, interceptor):
+        # TODO: Qt 5.6, URL Interceptor
+        pass
--- a/WebBrowser/Tools/DelayedFileWatcher.py	Sun Feb 21 19:54:14 2016 +0100
+++ b/WebBrowser/Tools/DelayedFileWatcher.py	Mon Feb 22 19:57:58 2016 +0100
@@ -3,71 +3,78 @@
 # Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
 #
 
+"""
+Module implementing a file system watcher with a delay.
+"""
+
 from __future__ import unicode_literals
 
-##class DelayedFileWatcher : public QFileSystemWatcher
-##{
-##    Q_OBJECT
-##
-##public:
-##    explicit DelayedFileWatcher(QObject* parent = 0);
-##    explicit DelayedFileWatcher(const QStringList &paths, QObject* parent = 0);
-##
-##signals:
-##    void delayedDirectoryChanged(const QString &path);
-##    void delayedFileChanged(const QString &path);
-##
-##private slots:
-##    void slotDirectoryChanged(const QString &path);
-##    void slotFileChanged(const QString &path);
-##
-##    void dequeueDirectory();
-##    void dequeueFile();
-##
-##private:
-##    void init();
-##
-##    QQueue<QString> m_dirQueue;
-##    QQueue<QString> m_fileQueue;
-##};
-##
-##
-##DelayedFileWatcher::DelayedFileWatcher(QObject* parent)
-##    : QFileSystemWatcher(parent)
-##{
-##    init();
-##}
-##
-##DelayedFileWatcher::DelayedFileWatcher(const QStringList &paths, QObject* parent)
-##    : QFileSystemWatcher(paths, parent)
-##{
-##    init();
-##}
-##
-##void DelayedFileWatcher::init()
-##{
-##    connect(this, SIGNAL(directoryChanged(QString)), this, SLOT(slotDirectoryChanged(QString)));
-##    connect(this, SIGNAL(fileChanged(QString)), this, SLOT(slotFileChanged(QString)));
-##}
-##
-##void DelayedFileWatcher::slotDirectoryChanged(const QString &path)
-##{
-##    m_dirQueue.enqueue(path);
-##    QTimer::singleShot(500, this, SLOT(dequeueDirectory()));
-##}
-##
-##void DelayedFileWatcher::slotFileChanged(const QString &path)
-##{
-##    m_fileQueue.enqueue(path);
-##    QTimer::singleShot(500, this, SLOT(dequeueFile()));
-##}
-##
-##void DelayedFileWatcher::dequeueDirectory()
-##{
-##    emit delayedDirectoryChanged(m_dirQueue.dequeue());
-##}
-##
-##void DelayedFileWatcher::dequeueFile()
-##{
-##    emit delayedFileChanged(m_fileQueue.dequeue());
-##}
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, QFileSystemWatcher, QTimer
+
+
+class DelayedFileWatcher(QFileSystemWatcher):
+    """
+    Class implementing a file system watcher with a delay.
+    
+    @signal delayedDirectoryChanged(path) emitted to indicate a changed
+        directory
+    @signal delayedFileChanged(path) emitted to indicate a changed file
+    """
+    delayedDirectoryChanged = pyqtSignal(str)
+    delayedFileChanged = pyqtSignal(str)
+    
+    def __init__(self, paths=None, parent=None):
+        """
+        Constructor
+        
+        @param paths list of paths to be watched
+        @type list of str
+        @param parent reference to the parent object
+        @type QObject
+        """
+        if paths:
+            super(DelayedFileWatcher, self).__init__(paths, parent)
+        else:
+            super(DelayedFileWatcher, self).__init__(parent)
+        
+        self.__dirQueue = []
+        self.__fileQueue = []
+        
+        self.directoryChanged.connect(self.__directoryChanged)
+        self.fileChanged.connect(self.__fileChanged)
+    
+    @pyqtSlot(str)
+    def __directoryChanged(self, path):
+        """
+        Private slot handling a changed directory.
+        
+        @param path name of the changed directory
+        @type str
+        """
+        self.__dirQueue.append(path)
+        QTimer.singleShot(500, self.__dequeueDirectory)
+    
+    @pyqtSlot(str)
+    def __fileChanged(self, path):
+        """
+        Private slot handling a changed file.
+        
+        @param path name of the changed file
+        @type str
+        """
+        self.__fileQueue.append(path)
+        QTimer.singleShot(500, self.__dequeueFile)
+    
+    @pyqtSlot()
+    def __dequeueDirectory(self):
+        """
+        Private slot to signal a directory change.
+        """
+        self.delayedDirectoryChanged.emit(self.__dirQueue.pop(0))
+    
+    @pyqtSlot()
+    def __dequeueFile(self):
+        """
+        Private slot to signal a file change.
+        """
+        self.delayedFileChanged.emit(self.__fileQueue.pop(0))
--- a/WebBrowser/Tools/Scripts.py	Sun Feb 21 19:54:14 2016 +0100
+++ b/WebBrowser/Tools/Scripts.py	Mon Feb 22 19:57:58 2016 +0100
@@ -54,7 +54,7 @@
                registerExternal(channel.objects.eric_object);
             }});
 
-            }})()"""
+        }})()"""
     
     return source.format(readAllFileContents(":/javascript/qwebchannel.js"))
 
@@ -226,7 +226,7 @@
             var val;
             {1}
             form.submit();
-            }})()"""
+        }})()"""
     
     valueSource = """
         val = document.createElement('input');
@@ -311,7 +311,7 @@
             });
             observer.observe(document.documentElement, { childList: true });
             
-            })()"""
+        })()"""
     return source
 
 
@@ -346,7 +346,7 @@
                 }}
             }}
             
-            }})()"""
+        }})()"""
     
     data = bytes(data).decode("utf-8")
     data = data.replace("'", "\\'")

eric ide

mercurial