qtqt4qtwebkitqwebelement

How to set value of input(type="file") with QWebElement?


I'm trying to upload a photo to vk.com using QtWebKit module. The problem I'm facing is inability to properly fill input(type="file")'s value. Here's some related code I use:

def upload():
    print 'uploading...'
    photoInput = web.page().mainFrame().documentElement().findFirst('input[id="photos_upload_input"]')
    assert photoInput, 'No input found'
    photoInput.setAttribute('value', '/Users/elmigranto/Downloads/stuff.png')

    print photoInput.evaluateJavaScript('return this.value;').toString()

It's reasonable to note, that filling value of file input is impossible from Javascript due to browser security policy. However, it should be possible using Qt API, more specifically, QWebElement::setAttribute() method. And that's what I did… with no effect (well, photoInput.attribute('value') returns expected result, but photoInput.evaluateJavaScript('return this.value;').toString() returns empty string, input's onchange handler is also not triggered).

Setting other attributes is no problem, for example, QWebElement::addClass() works like a charm.

Any help would be super great.
Thanks.


Solution

  • The setAttribute method might still not work for security reasons.

    But you can redefine the function QWebPage::chooseFile that should normally open the upload dialog and return the filename so that it returns a static file name without opening the dialog, and activate that upload by simulating a "return" key press on the input element.

    This seems to work:

    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
    from PyQt4.QtWebKit import *
    import sys
    
    class WebPage(QWebPage):
        def __init__(self, parent = None):
            super(WebPage, self).__init__(parent)
            self.overrideUpload = None
    
        def chooseFile(self, originatingFrame, oldFile):
            if self.overrideUpload is None:
                return super(WebPage, self).chooseFile(originatingFrame, oldFile)
            result = self.overrideUpload
            self.overrideUpload = None
            return result
    
        def setUploadFile(self, selector, filename):
            button = self.mainFrame().documentElement().findFirst(selector)
            self.overrideUpload = filename
            # set the focus on the input element
            button.setFocus();
            # and simulate a keypress event to make it call our chooseFile method 
            webview.event(QKeyEvent(QEvent.KeyPress, Qt.Key_Enter, Qt.NoModifier))
    
    def upload():
        print 'uploading...'    
        page.setUploadFile('input[id="photos_upload_input"]',
            '/Users/elmigranto/Downloads/stuff.png') 
        # The change seems to be asynchronous, at it isn't visible 
        # just after the previous call
    
    app = QApplication(sys.argv)
    webview = QWebView()
    page = WebPage(webview)
    webview.setPage(page)
    source = '''
    <form action="#">
      Select a file: <input type="file" id="photos_upload_input">
      <input type="submit">
    </form>
    '''
    webview.loadFinished.connect(upload)
    webview.show()
    webview.setHtml(source)
    sys.exit(app.exec_())