Simulate mouse hover in PyQt5
Question:
How do you simulate mouse hover for PyQt5 to a coordinate? Perhaps with QPoint.
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import Qt, QUrl, QTimer
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
class Screenshot(QWebEngineView):
def capture(self, url, output_file):
self.output_file = output_file
self.load(QUrl(url))
self.loadFinished.connect(self.on_loaded)
# Create hidden view without scrollbars
#self.setAttribute(Qt.WA_DontShowOnScreen)
self.page().settings().setAttribute(
QWebEngineSettings.ShowScrollBars, False)
self.show()
def on_loaded(self):
size = self.page().contentsSize().toSize()
self.resize(size)
# Wait for resize
print("Capturing")
QTimer.singleShot(3000, self.take_screenshot)
def take_screenshot(self):
self.grab().save(self.output_file, b'PNG')
self.app.quit()
app = QApplication(sys.argv)
screenshots = {}
screenshot = Screenshot()
screenshot.app = app
screenshot.capture('https://zoom.earth/maps/wind-speed/#view=13.955336,121.109689,11z', 'wind.png')
Such that it shows a UI overlay like in the image.
Zoom Wind map, Mouse hovered at center
Answers:
Faking a mouse event is not that difficult, it’s just a matter of creating a QMouseEvent with the correct arguments:
event = QMouseEvent(QEvent.MouseMove,
QPoint(someX, someY),
Qt.NoButton,
Qt.MouseButtons(Qt.NoButton),
Qt.KeyboardModifiers(Qt.NoModifier))
- the QPoint argument represents the point in which the cursor is supposed to be, in local coordinates: in the following example I’ll use
self.rect().center()
, which is the exact center in the rectangle of the viewport;
- the next argument is the mouse button that generates the QMouseEvent; since a mouse move event is not generated by a mouse button press, it should always be
NoButton
;
. the argument after represents the buttons that are pressed at the time of the event; since we suppose that this is just a "hover" movement, we still have a NoButton
value (but using the MouseButton
flag, since it represents a flag and multiple buttons could be pressed);
- the last is obviously about the keyboard modifiers, and we again don’t need any of that, but still we must use a flag;
The important part is to send the event to the correct widget. QWebEngineView uses a private QWidget subclass to show the actual content and receive user interaction, so we need to find that widget.
def on_loaded(self):
size = self.page().contentsSize().toSize()
self.resize(size)
# Get the actual web content widget
child = self.findChild(QWidget)
# Create a fake mouse move event
event = QMouseEvent(QEvent.MouseMove,
self.rect().center(),
Qt.NoButton, Qt.MouseButtons(Qt.NoButton),
Qt.KeyboardModifiers(Qt.NoModifier))
# Send the event
QApplication.postEvent(child, event)
# Wait for resize
print("Capturing")
QTimer.singleShot(3000, self.take_screenshot)
Working Code:
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import Qt, QUrl, QTimer
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
class Screenshot(QWebEngineView):
def capture(self, url, output_file):
self.output_file = output_file
self.load(QUrl(url))
self.loadFinished.connect(self.on_loaded)
# Create hidden view without scrollbars
#self.setAttribute(Qt.WA_DontShowOnScreen)
self.page().settings().setAttribute(
QWebEngineSettings.ShowScrollBars, False)
self.show()
def on_loaded(self):
size = self.page().contentsSize().toSize()
self.resize(size)
# Wait for resize
print("Capturing")
QTimer.singleShot(3000, self.take_screenshot)
def take_screenshot(self):
self.grab().save(self.output_file, b'PNG')
self.app.quit()
app = QApplication(sys.argv)
screenshots = {}
screenshot = Screenshot()
screenshot.app = app
screenshot.capture('https://zoom.earth/maps/wind-speed/#view=13.955336,121.109689,11z', 'wind.png')
How do you simulate mouse hover for PyQt5 to a coordinate? Perhaps with QPoint.
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import Qt, QUrl, QTimer
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
class Screenshot(QWebEngineView):
def capture(self, url, output_file):
self.output_file = output_file
self.load(QUrl(url))
self.loadFinished.connect(self.on_loaded)
# Create hidden view without scrollbars
#self.setAttribute(Qt.WA_DontShowOnScreen)
self.page().settings().setAttribute(
QWebEngineSettings.ShowScrollBars, False)
self.show()
def on_loaded(self):
size = self.page().contentsSize().toSize()
self.resize(size)
# Wait for resize
print("Capturing")
QTimer.singleShot(3000, self.take_screenshot)
def take_screenshot(self):
self.grab().save(self.output_file, b'PNG')
self.app.quit()
app = QApplication(sys.argv)
screenshots = {}
screenshot = Screenshot()
screenshot.app = app
screenshot.capture('https://zoom.earth/maps/wind-speed/#view=13.955336,121.109689,11z', 'wind.png')
Such that it shows a UI overlay like in the image.
Zoom Wind map, Mouse hovered at center
Faking a mouse event is not that difficult, it’s just a matter of creating a QMouseEvent with the correct arguments:
event = QMouseEvent(QEvent.MouseMove,
QPoint(someX, someY),
Qt.NoButton,
Qt.MouseButtons(Qt.NoButton),
Qt.KeyboardModifiers(Qt.NoModifier))
- the QPoint argument represents the point in which the cursor is supposed to be, in local coordinates: in the following example I’ll use
self.rect().center()
, which is the exact center in the rectangle of the viewport; - the next argument is the mouse button that generates the QMouseEvent; since a mouse move event is not generated by a mouse button press, it should always be
NoButton
;
. the argument after represents the buttons that are pressed at the time of the event; since we suppose that this is just a "hover" movement, we still have aNoButton
value (but using theMouseButton
flag, since it represents a flag and multiple buttons could be pressed); - the last is obviously about the keyboard modifiers, and we again don’t need any of that, but still we must use a flag;
The important part is to send the event to the correct widget. QWebEngineView uses a private QWidget subclass to show the actual content and receive user interaction, so we need to find that widget.
def on_loaded(self):
size = self.page().contentsSize().toSize()
self.resize(size)
# Get the actual web content widget
child = self.findChild(QWidget)
# Create a fake mouse move event
event = QMouseEvent(QEvent.MouseMove,
self.rect().center(),
Qt.NoButton, Qt.MouseButtons(Qt.NoButton),
Qt.KeyboardModifiers(Qt.NoModifier))
# Send the event
QApplication.postEvent(child, event)
# Wait for resize
print("Capturing")
QTimer.singleShot(3000, self.take_screenshot)
Working Code:
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import Qt, QUrl, QTimer
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
class Screenshot(QWebEngineView):
def capture(self, url, output_file):
self.output_file = output_file
self.load(QUrl(url))
self.loadFinished.connect(self.on_loaded)
# Create hidden view without scrollbars
#self.setAttribute(Qt.WA_DontShowOnScreen)
self.page().settings().setAttribute(
QWebEngineSettings.ShowScrollBars, False)
self.show()
def on_loaded(self):
size = self.page().contentsSize().toSize()
self.resize(size)
# Wait for resize
print("Capturing")
QTimer.singleShot(3000, self.take_screenshot)
def take_screenshot(self):
self.grab().save(self.output_file, b'PNG')
self.app.quit()
app = QApplication(sys.argv)
screenshots = {}
screenshot = Screenshot()
screenshot.app = app
screenshot.capture('https://zoom.earth/maps/wind-speed/#view=13.955336,121.109689,11z', 'wind.png')