初始evaluateJavaScript()调用后出现PyQt4 QWebview错误

时间:2015-11-01 21:05:43

标签: python qt pyqt pyqt4 qwebview

我有一个运行PyQt4的Python 2.7应用程序,里面有一个QWebView,它与Javascript之间有双向通信。

应用程序通过QThreadPool,QRunnables进行多线程处理,因此我正在与带有信号的ViewController类进行通信。

当我运行应用程序时,QWebView加载我的HTML与外部JS和CSS就好了。我可以通过主程序线程和ViewController类与Javascript函数进行交互。

一旦用户选择目录并满足某些条件,它就会开始逐个循环执行QRunnable任务。在此期间,它回调ViewController - >通过信号插槽的Javascript,正如预期的那样。问题是当我调用执行evaluateJavaScript的ViewController方法时,我得到了一个返回的Javascript错误,

  

未定义的第1行:SyntaxError:解析错误

我来回做了很多试验错误,但似乎无法弄清楚为什么evaluateJavaScript不会在这些实例中运行。我尝试发送简单的Javascript调用,范围从不接受任何参数的测试函数(想想可能是一些奇怪的编码问题),只发送像Application.main.evaluateJavaScript("alert('foo')")这样的东西,它通常在线程之外工作。我能想到的另一件事是,可能需要再次在线程中调用self.main.addToJavaScriptWindowObject('view', self.view),但我在Application.main上运行了一个dir(),它似乎已经附加了evaluateJavaScript方法

关于为什么会发生这种情况的任何想法,当范围看起来是正确的,并且ViewController似乎正好与QWebView进行通信? Qt C ++中的答案可能也会起作用,如果你之前已经看到过这种情况!

我试图为了示例目的简化代码:

# coding: utf8
import subprocess as sp

import os.path, os, sys, time, datetime
from os.path import basename
import glob

import random
import string

from PyQt4 import QtCore, QtGui, QtWebKit
from PyQt4.QtCore import QObject, pyqtSlot, QThreadPool, QRunnable, pyqtSignal
from PyQt4.QtGui import QApplication, QFileDialog
from PyQt4.QtWebKit import QWebView
from ImportController import *


class Browser(QtGui.QMainWindow):

    def __init__(self):

        QtGui.QMainWindow.__init__(self)
        self.resize(800,500)
        self.centralwidget = QtGui.QWidget(self)

        self.mainLayout = QtGui.QHBoxLayout(self.centralwidget)
        self.mainLayout.setSpacing(0)
        self.mainLayout.setMargin(0)

        self.frame = QtGui.QFrame(self.centralwidget)

        self.gridLayout = QtGui.QVBoxLayout(self.frame)
        self.gridLayout.setMargin(0)
        self.gridLayout.setSpacing(0)

        self.html = QtWebKit.QWebView()

        # for javascript errors
        errors = WebPage()
        self.html.setPage(errors)

        self.main = self.html.page().mainFrame()
        self.gridLayout.addWidget(self.html)
        self.mainLayout.addWidget(self.frame)
        self.setCentralWidget(self.centralwidget)

        path = os.getcwd()

        if self.checkNetworkAvailability() and self.checkApiAvailbility():
            self.default_url = "file://"+path+"/View/mainView.html"
        else:
            self.default_url = "file://"+path+"/View/errorView.html"

        # load the html view
        self.openView()

        # controller class that sends and receives to/from javascript
        self.view = ViewController()
        self.main.addToJavaScriptWindowObject('view', self.view)

        # on gui load finish
        self.html.loadFinished.connect(self.on_loadFinished)

    # to javascript

    def selectDirectory(self):
        # This evaluates the directory we've selected to make sure it fits the criteria, then parses the XML files
        pass


    def evaluateDirectory(self, directory):

        if not directory:
            return False

        if os.path.isdir(directory):
            return True
        else:
            return False

    @QtCore.pyqtSlot()
    def on_loadFinished(self):

        # open directory select dialog
        self.selectDirectory()

    def openView(self):

        self.html.load(QtCore.QUrl(self.default_url))
        self.html.show()

    def checkNetworkAvailability(self):
        #TODO: make sure we can reach the outside world before trying anything else
        return True

    def checkApiAvailbility(self):
        #TODO: make sure the API server is alive and responding
        return True


class WebPage(QtWebKit.QWebPage):
    def javaScriptConsoleMessage(self, msg, line, source):
        print '%s line %d: %s' % (source, line, msg)

class ViewController(QObject):
    def __init__(self, parent=None):
        super(ViewController, self).__init__(parent)

    @pyqtSlot()
    def did_load(self):
        print "View Loaded."

    @pyqtSlot()
    def selectDirectoryDialog(self):
        # FROM JAVASCRIPT: in case they need to re-open the file dialog
        Application.selectDirectory()

    def prepareImportView(self, displayPath):
        # TO JAVASCRIPT: XML directory parsed okay, so let's show the main
        Application.main.evaluateJavaScript("prepareImportView('{0}');".format(displayPath))

    def generalMessageToView(self, target, message):
        # TO JAVASCRIPT: Send a general message to a specific widget target
        Application.main.evaluateJavaScript("receiveMessageFromController('{0}', '{1}')".format(target, message))


    @pyqtSlot()
    def startProductImport(self):
        # FROM JAVASCRIPT: Trigger the product import loop, QThreads
        print "### view.startProductImport"
        position = 1
        count = len(Application.data.products)

        importTasks = ProductImportQueue(Application.data.products)
        importTasks.start()

    @pyqtSlot(str)
    def updateProductView(self, data):
        # TO JAVASCRIPT: Send product information to view
        print "### updateProductView "
        Application.main.evaluateJavaScript('updateProductView("{0}");'.format(QtCore.QString(data)) )


class WorkerSignals(QObject):
    ''' Declares the signals that will be broadcast to their connected view methods '''
    productResult = pyqtSignal(str)

class ProductImporterTask(QRunnable):
    ''' This is where the import process will be fired for each loop iteration '''
    def __init__(self, product):
        super(ProductImporterTask, self).__init__()

        self.product = product
        self.count = ""
        self.position = ""
        self.signals = WorkerSignals()

    def run(self):
        print "### ProductImporterTask worker {0}/{1}".format(self.position, self.count)

        # Normally we'd create a dict here, but I'm trying to just send a string for testing purposes
        self.signals.productResult.emit(data)

        return

class ProductImportQueue(QObject):
    ''' The synchronous threadpool that is going to one by one run the import threads '''
    def __init__(self, products):
        super(ProductImportQueue, self).__init__()

        self.products = products
        self.pool = QThreadPool()
        self.pool.setMaxThreadCount(1)

    def process_result(self, product):
        return

    def start(self):
        ''' Call the product import worker from here, and format it in a predictable way '''

        count = len(self.products)
        position = 1
        for product in self.products:

            worker = ProductImporterTask("test")

            worker.signals.productResult.connect(Application.view.updateProductView, QtCore.Qt.DirectConnection)
            self.pool.start(worker)
            position = position + 1

        self.pool.waitForDone()




if __name__ == "__main__":

    app = QtGui.QApplication(sys.argv)
    Application = Browser()
    Application.raise_()
    Application.show()
    Application.activateWindow()
    sys.exit(app.exec_())

1 个答案:

答案 0 :(得分:0)

你知道,我喜欢PyQt4,但经过搜索和搜索,我相信这实际上是一个错误而不是设计。

我已经开始尝试使用WxPython在CEFPython中实现这一点,WxPython似乎为此特定目的提供了更优雅的实现。