2015-11-01 27 views
1

我有一個Python 2.7應用程序與PyQt4一起運行,它有一個QWebView,它與JavaScript之間有雙向通信。PyQt4在初始evaluateJavaScript()調用後QWebview錯誤

該應用程序是通過QThreadPool,QRunnables多線程的,所以我用信號與ViewController類進行通信。

當我運行應用程序時,QWebView加載我的HTML與外部JS和CSS就好了。我可以通過主程序線程和ViewController類與Javascript函數交互。

一旦用戶選擇一個目錄並滿足一定的標準,它就開始循環一次一個QRunnable任務。在那段時間內,它會像預期的那樣回調到ViewController - > Javascript via Signal slots。問題是,當我打電話了執行evaluateJavaScript,我得到一個JavaScript錯誤返回這些視圖控制器方法,

undefined line 1: SyntaxError: Parse error

我已經做了很多試驗錯誤的來回,但似乎無法找出原因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_()) 

回答

0

你知道,我愛PyQt4的,但尋尋覓覓之後,我認爲這其實是一個錯誤,不是有意設計的。

我已經繼續前進了,並試圖用WxPython在CEFPython中實現這一點,WxPython似乎有一個更加優雅的實現來實現這個特定的目的。

+0

*我已經嘗試了PyQt5,它的價值。 –