Qt: Take over system open file dialog - python

I would like to automate the upload of a file to a website using PyQt4's QWebView, but there's a part I can't figure out yet. To upload the file, the website has a button, which opens a dialog from which you are supposed to select the local file. So, these are my questions :) Is there a way to control that dialog once I click the button? Is there a better way to achieve this?
edit
The website is https://maps.google.com/ and I'm uploading a .kml file through My Places > Create Map > Import.

It is possible what you're looking for is QWebPage::chooseFile() (I suppose it depends also on how the website is handling that). Reimplement that and see if it is sufficient. Do whatever you want and return the chosen file path.
EDIT: Now that you provided the link I tested and seems to work.

Ok, to begin let me start with background information and references.
The module that I will be using is pywin32 download here, specifically the win32gui, API reference here.
Now before you can manipulate the dialog you have to "navigate" to the window handle, the following uses win32.FindWindow API Reference here, which looks like this, where the two inputs are the lpclassName in this case #32770 (stands for a dialog) reference here and the lpWindowName which in this case is File Upload,
HWND WINAPI FindWindow(
_In_opt_ LPCTSTR lpClassName,
_In_opt_ LPCTSTR lpWindowName
);
Code to locate file handle:
import win32gui
control = win32gui.FindWindow("#32770", "File Upload")
And it stores the handle, which in my case was 721470.
The next step is locate the handles of the GUI objects in the dialog, i will show an example of the Cancel button. To find the handle, I wil be using FindWindowEx API reference here,
import win32con
import win32api
ButtonHandle = win32gui.FindWindowEx(control, 0, "Button", "Cancel");
win32api.SendMessage(ButtonHandle, win32con.BM_CLICK, 0, 0)
Reference here for the BM_CLICK and here for the SendMessage.
Final code:
import win32gui
import win32api
import win32con
window = win32gui.GetForegroundWindow()
title = win32gui.GetWindowText(window)
control = win32gui.FindWindow("#32770", "File Upload")
ButtonHandle = win32gui.FindWindowEx(control, 0, "Button", "Cancel")
win32api.SendMessage(ButtonHandle, win32con.BM_CLICK, 0, 0)
Another way is to use the watsup.winGuiAuto module, here, example below:
from watsup.winGuiAuto import *
optDialog = findTopWindow(wantedText="File Upload")
CancelButton = findControl(optDialog,wantedClass="Button", wantedText="Cancel")
clickButton(SaveButton)
But i believe the easiest way is to use autoit here, i have used it before in pyqt, to shoot out commands.
Hope this helps!
Additional References (pywin32 versions):
win32gui here
win32api here

Here's a pure PyQt4 demo that more or less reproduces the default implementation:
import sys
from PyQt4 import QtCore, QtGui, QtWebKit
class WebPage(QtWebKit.QWebPage):
def chooseFile(self, frame=None, path=''):
return QtGui.QFileDialog.getOpenFileName(self.parent(), '', path)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
view = QtWebKit.QWebView()
view.setPage(WebPage(view))
view.load(QtCore.QUrl('https://maps.google.com/'))
view.show()
sys.exit(app.exec_())

Related

Enable secure playback, Widevine DRM in python webbrowsers (Protected content)

I'm trying to use the spotify web player on a python browser windows.
I try it in two ways:
import webview
webview.create_window('spotify', 'https://open.spotify.com')
webview.start()
import sys
import time
from PyQt5.Qt import *
from PyQt5.QtWebEngineWidgets import *
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
web = QWebEngineView()
web.load(QUrl("https://open.spotify.com/"))
web.showFullScreen()
sys.exit(app.exec_())
After I log in with my account I get:
Enable secure playback in your browser.
Is there way I can do this? In the spotify support page it tells me to enable protected content playback or play drm. I am not sure if it is possible.
By #metatoaster 's suggestion, I tried to set the QTWEBENGINE_CHROMIUM_FLAGS environment variable to --widevine-path="/home/<myusername>/.mozilla/firefox/237oaqbb.default-release/gmp-widevinecdm/4.10.2449.0/libwidevinecdm.so"
After I got the location from locate libwidevinecdm.so, and it worked!
I tried with the spotify's path for the library but that didn't work.

Pressing button in QFileDialog popup exits application

I made the transition from PyQt4to PyQt5. My app (created with QtDesigner) has a checkbox which enables a "Save" button, in case you want to save your file. In PyQt4 the dialog would open, I'd choose my file, press OK, done. I implemented a check on the OK button of the main application that would prompt an error if the path was invalid, e.g. if you pressed cancel in the QFileDialog.
With PyQt5 my application exits completely if I close the QFileDialog in any way (OK, cancel, X). I want just the QFileDialog to close and not my main window. How do I do this? Thanks for your time and help.
Here's the relevant part of my code:
self.path = self.ui.savepathButton.pressed.connect(lambda: self.file_save())
def file_save(self):
path = QFileDialog.getSaveFileName(self, "Choose a path and filename", os.getcwd().replace("\\", "/") +
"/test.stl", filter="Stereolithography Files (*.stl)")
self.ui.savepath_label.setText(path) <------ NO ERROR WITHOUT THIS LINE
def OKButton_click(self):
if os.path.isdir(os.path.split(self.ui.savepath_label.text())[0]) is False:
# Warning if the filename is invalid.
file_error = QMessageBox()
file_error.setIcon(QMessageBox.Warning)
file_error.setText("Invalid path or filename.")
file_error.setInformativeText("Please choose a working path and filename.") file_error.setWindowTitle("File name error")
file_error.setStandardButtons(QMessageBox.Ok)
file_error.exec_()
else:
self.accept()
Edit:
I know where my error is located, but I still cannot fix it. I marked the line in the code. Why does self.ui.savepath_label.setText(path) terminate my application?
The PyQt4 provides two different APIs:
API v1 uses the Qt types for objects, so you have to pass things like QString to the setText method
API v2 instead uses python types and the methods of the Qt objects automatically convert those python types into their Qt variants, so you have to pass a python str to them.
This is mentioned in this page about PyQt4. PyQt5 only supports version 2 of the API (that page also mentions other differences).
Also note that according to the question pyqt5 - finding documentation the PyQt5 method getSaveFileName actually returns a pair of (filename, filter) so it's effectively equivalent to PyQt4's getSaveFileNameAndFilter method, which means that you could simply use:
self.ui.savepath_label.setText(path[0])
To set the text. Minimal complete example:
from PyQt5.QtWidgets import QFileDialog, QWidget, QApplication, QHBoxLayout, QPushButton
class Window(QWidget):
def __init__(self):
super(Window, self).__init__(None)
layout = QHBoxLayout()
self.button = QPushButton('click')
layout.addWidget(self.button)
self.setLayout(layout)
self.button.clicked.connect(self.ask_filename)
def ask_filename(self):
fname = QFileDialog.getSaveFileName(self, 'title')
print(fname)
self.button.setText(fname[0])
app = QApplication([])
window = Window()
window.show()
app.exec_()
By the way, if you change fname[0] to fname and try to launch this application from the terminal you get the following helpful error message:
Traceback (most recent call last):
File "test_qt.py", line 15, in ask_filename
self.button.setText(fname)
TypeError: QAbstractButton.setText(str): argument 1 has unexpected type 'tuple'
which tells you that the return type of getSaveFileName is a tuple and not a str.
I finally found the (very small) error:
While PyQt4 apparently writes the path automatically as string, PyQt5 does not.
I changed
self.ui.savepath_label.setText(path)
into
self.ui.savepath_label.setText(str(path))
and all is good now.

Is it possible to get QWebKit to display pdf files?

i have a link in my QWebkit, which points to a pdf file.
But when the link is clicked, it can't display the pdf file.
is there a way to make it happen?
If you enable plugins through QWebSettings and have a PDF viewer installed that provides a browser plugin such as Acrobat then you should see the PDF rendered using the plugin inside your QWebView:
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtWebKit import *
app = QApplication(sys.argv)
web = QWebView()
web.settings().setAttribute(QWebSettings.PluginsEnabled, True)
web.show()
web.load(QUrl('file:///C:/test/test.pdf')) # Change path to actual file.
sys.exit(app.exec_())
This code isn't working for me on Windows with the latest version of Acrobat X (it just shows a progress bar but no PDF - proof that the plugin is loading, just not working) but I'm sure this is how I've done it before. Give it a try and let me know.
Webkit doesn't include a PDF viewer. You'll need to have some way of rendering it - whether you pass it off to a different viewer (Adobe PDF viewer or something else), render it in the control in some way you devise (you could even try rendering it in JavaScript for fun).
This is Gary Hudges code for PyQt5:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtWebKitWidgets import *
from PyQt5.QtWebKit import *
from PyQt5.QtCore import *
app = QApplication(sys.argv)
web = QWebView()
web.settings().setAttribute(QWebSettings.PluginsEnabled, True)
web.show()
web.load(QUrl('file:///C:/data/progetti_miei/python/test.pdf')) # Change path to actual file.
sys.exit(app.exec_())

Get text from popup window

I'm trying to read the text from a popup window.
The title is always the same. I've managed to identify the hwnd and get the title with the code below, but I can't figure out how to read the contents.
import time
import win32gui, win32con
windows = []
def _MyCallback( hwnd, extra ):
extra.append(hwnd)
win32gui.EnumWindows(_MyCallback, windows)
while True:
window = win32gui.GetForegroundWindow()
title = win32gui.GetWindowText(window)
if title == 'Errors occurred': print 'error window'
time.sleep(1)
Here's the working version:
import time
import win32gui
while True:
window = win32gui.GetForegroundWindow()
title = win32gui.GetWindowText(window)
if title == 'Errors occurred':
control = win32gui.FindWindowEx(window, 0, "static", None)
print 'text: ', win32gui.GetWindowText(control)
time.sleep(1)
You will only be able to read this text programmatically if it is contained in a windowed control. You can easily check this with Spy++. Many GUI frameworks don't use windowed controls for their child controls, or only use windowed controls for some children.
If it is a windowed control then you can identify it by calling GetWindow() and walking the child structure (obviously you need to use the win32gui equivalent).
I don't have access to the framework or the error dialog you are using, so I can only say in general what you want.
You need the FindWindowEx function, and use it to find a control whose class name is 'static' (or whatever the class name of the control is). I imagine this would be the line:
control = win32gui.FindWindowEx(window, 0, "Static", 0)
That returns the handle to the control, and you can then use GetWindowText on that to get the text.

Website to image

I'm running Python 3.1 and you would call me an advanced novice :)
My question is simple: I'm trying to make a simple program which asks the users for a URL (or multiple URLs) and then goes to the website and takes a screenshot (of the whole page, not just what can be seen in the browser without scrolling all the way down).
It's simpler then it sounds, I want to use an existing platform on the web, similar to this:
import subprocess
MYFILENAME = "google_screen"
MYURL = "www.google.com"
subprocess.Popen(['wget', '-O', MYFILENAME+'.png', 'http://images.websnapr.com/?url='+MYURL+'&size=s&nocache=82']).wait()
Although this website does not work :(, I'm wondering is it possible to do it with this website and if so, how? If it is not possible, are there any alternatives?
There is a package called webkit2png that you can use for this, its located: here
More information on this blog post
Example from blog post(copied to SO for preservation, read the blog post to understand it if you have issues):
#!/usr/bin/env python
import sys
import signal
from PyQt4.QtCore import *
from PyQt 4.QtGui import *
from PyQt4.QtWebKit import QWebPage
def onLoadFinished(result):
if not result:
print "Request failed"
sys.exit(1)
# Set the size of the (virtual) browser window
webpage.setViewportSize(webpage.mainFrame().contentsSize())
# Paint this frame into an image
image = QImage(webpage.viewportSize(), QImage.Format_ARGB32)
painter = QPainter(image)
webpage.mainFrame().render(painter)
painter.end()
image.save("output2.png")
sys.exit(0)
app = QApplication(sys.argv)
signal.signal(signal.SIGINT, signal.SIG_DFL)
webpage = QWebPage()
webpage.connect(webpage, SIGNAL("loadFinished(bool)"), onLoadFinished)
webpage.mainFrame().load(QUrl("http://www.google.com"))
sys.exit(app.exec_())
Edit: Link to the pyqt4 download page
You can use Selenium to get a screenshot, but it'll only be what is viewed by the browser.

Categories

Resources