4

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.

Aleksei Zabrodskii
  • 2,220
  • 3
  • 19
  • 41

1 Answers1

6

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_())
alexisdm
  • 29,448
  • 6
  • 64
  • 99
  • 1
    How would the webview.event(QKeyEvent(QEvent.KeyPress, Qt.Key_Enter, Qt.NoModifier)) be translated into its C++ equivalent ? – hytromo Oct 19 '13 at 10:22
  • 2
    @user9379 This should work: `QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier); webview->event(&keyEvent);`. – alexisdm Oct 20 '13 at 23:07
  • Can I do this using `WebKit.NET` or `CefSharp`? I search a lot but I can't find the equivalent object to `QWebPage` or `setPage()` method – Jack Sep 03 '15 at 22:24
  • @Jack Do you really need to simulate user input ? Can't you create a POST query manually with a HttpWebRequest ? (like [that](http://stackoverflow.com/q/566462/894321) ) – alexisdm Sep 04 '15 at 11:28
  • @alexisdm: disregard previously comment, how do I set the input type = file opened in current page? – Jack Sep 05 '15 at 07:02