Simulate mouse hover in PyQt5 - python

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 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)

Related

How to detect mouse hover event in PySide6 widget

I am trying to create a custom image viewer widget with zoom to the mouse position. So far I have managed to detect mouse scroll events, but I cannot detect a mouse hover events so I could determine a mouse position to zoom to.
I seems to me that the mouse hover event is not even occurring. I tried to print out all events, but the QHoverEvent is just not there. The only event occurring during mouse hover is QEvent::ToolTip which has the mouse position but it only occurs after the mouse hovering stops and it has quite a delay (~0.5s).
Here is the code:
import sys
from PySide6 import QtWidgets
from PySide6.QtWidgets import QDialog, QVBoxLayout, QLabel
from PySide6.QtGui import QPixmap
from PySide6.QtCore import Qt
from PIL.ImageQt import ImageQt
class ImageViewer(QDialog):
def eventFilter(self, object, event):
print("Event:" + str(event))
if str(event) == '<PySide6.QtGui.QWheelEvent(Qt::NoScrollPhase, pixelDelta=QPoint(0,0), angleDelta=QPoint(0,-120))>':
print("detected zoom out")
if str(event) == '<PySide6.QtGui.QWheelEvent(Qt::NoScrollPhase, pixelDelta=QPoint(0,0), angleDelta=QPoint(0,120))>':
print("detected zoom in")
if str(event) == '<PySide6.QtCore.QEvent(QEvent::ToolTip)>':
print("detected tooltip")
return True
def __init__(self, img: ImageQt):
super().__init__()
self.setWindowTitle('Image viewer example')
self.imageWidget = QLabel()
self.imageWidget.setAlignment(Qt.AlignCenter)
self.imageWidget.setPixmap(QPixmap.fromImage(img))
self.layout = QVBoxLayout()
self.layout.addWidget(self.imageWidget)
self.setLayout(self.layout)
self.imageWidget.installEventFilter(self)
if __name__ == '__main__':
# prepare app
app = QtWidgets.QApplication(sys.argv)
# create viewer widget
imageViewer = ImageViewer(ImageQt("img.png"))
imageViewer.show()
# close app
sys.exit(app.exec())
I am able to detect the mouse scrolling, enter widget, leave, mouse button press/release, mouse move (with mouse pressed). But the mouse hover is just not there. Could someone tell me how to detect the mouse hover event (with mouse position info)?
It works for me with self.setAttribute(Qt.WidgetAttribute.WA_Hover) in the __init__() constructor.

Emit a signal when any button is pressed

In a pyqt5 app I want to untoggle a button when any other button is clicked.
Is it possible to have a signal emitted when any button inside a layout or a widget is clicked?
I can connect each button click to an untoggle function but that would be hard to maintain if the buttons ever change. Is there a clean way to achieve the same functionality without having to connect the buttons one by one?
How to point out a possible solution is to inherit from the button and implement the indicated logic, but in the case of Qt it can be taken advantage of the fact that the clicked signal is associated with the QEvent.MouseButtonRelease event when a button is pressed. Considering the above, notify can be used to implement the logic:
import sys
from PyQt5.QtCore import pyqtSignal, QCoreApplication, QEvent
from PyQt5.QtWidgets import (
QAbstractButton,
QApplication,
QPushButton,
QVBoxLayout,
QWidget,
)
class Application(QApplication):
buttonClicked = pyqtSignal(QAbstractButton)
def notify(self, receiver, event):
if (
isinstance(receiver, QAbstractButton)
and receiver.isEnabled()
and event.type() == QEvent.MouseButtonRelease
):
self.buttonClicked.emit(receiver)
return super().notify(receiver, event)
def main(argv):
app = Application(argv)
view = QWidget()
lay = QVBoxLayout(view)
special_button = QPushButton("Special")
lay.addWidget(special_button)
for i in range(10):
button = QPushButton(f"button{i}")
lay.addWidget(button)
view.show()
def handle_clicked(button):
if button is not special_button:
print(f"{button} clicked")
QCoreApplication.instance().buttonClicked.connect(handle_clicked)
sys.exit(app.exec_())
if __name__ == "__main__":
main(sys.argv)
What you want is added functionality for an existing type. This suggests that you want to write an extension of the Button class. Write a class that inherits from Button. Write your own function to handle button clicks: this function will call your toggle function and then call the parent's button click handler.

LineEdit box in PyQt5

When I press the Key Enter in the lineEdit box performs both function enter_LineEdit() and function click_Edit(). Why does it perform function click_Edit()? It must not!
I would like someone to explain to me why does it work this way?
#!/usr/bin/python3.6
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QMainWindow,QApplication,QPushButton,QDialog,QHBoxLayout,QLabel,QWidget,QLineEdit
from PyQt5 import QtGui
from PyQt5 import QtCore
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.setGeometry(100,100,600,400)
self.CreateBtn()
self.show()
def CreateBtn(self):
button = QPushButton("Second Window", self)
button.setGeometry(QtCore.QRect(30,100,200,80))
button.setIconSize(QtCore.QSize(70,70))
button.clicked.connect(self.SecWin)
def SecWin(self):
self.d = SecondWindow()
self.d.Create_SecWin()
self.d.Create_Object()
self.d.Create_Layout()
class SecondWindow(QDialog):
def Create_SecWin(self):
self.setGeometry(600,360,400,100)
self.show()
def Create_Object(self):
self.btnEdit = QPushButton("Edit",self)
self.btnEdit.clicked.connect(self.click_Edit)
self.labelSearch = QLabel("Search:",self)
self.lineEdit = QLineEdit(self)
self.lineEdit.returnPressed.connect(self.enter_LineEdit)
def Create_Layout(self):
hbox1 = QHBoxLayout()
hbox1.addWidget(self.btnEdit)
hbox1.addWidget(self.labelSearch)
hbox1.addWidget(self.lineEdit)
self.setLayout(hbox1)
def click_Edit(self):
print("Philip")
def enter_LineEdit(self):
print("Karl")
App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec_())
If you review the docs of the autoDefault property of QPushButton:
This property holds whether the push button is an auto default button
If this property is set to true then the push button is an auto
default button.
In some GUI styles a default button is drawn with an extra frame
around it, up to 3 pixels or more. Qt automatically keeps this space
free around auto-default buttons, i.e., auto-default buttons may have
a slightly larger size hint.
This property's default is true for buttons that have a QDialog
parent; otherwise it defaults to false.
See the default property for details of how default and auto-default
interact.
And also from the default property:
[...]
A button with this property set to true (i.e., the dialog's default
button,) will automatically be pressed when the user presses enter,
with one exception: if an autoDefault button currently has focus, the
autoDefault button is pressed. When the dialog has autoDefault buttons
but no default button, pressing enter will press either the
autoDefault button that currently has focus, or if no button has
focus, the next autoDefault button in the focus chain.
[...]
That is, some of the QPushButtons will be pressed when the enter key is pressed in a QDialog since all QPushButtons have the autoDefault property in True, so the solution is to set it to False:
self.btnEdit = QPushButton("Edit", self)
self.btnEdit.setAutoDefault(False)

Getting Information Which QLabel Clicked in PyQT

I have a list of QLabel and want to learn which QLabel clicked . When i look for the making QLabel clickable , this code has worked :
labels[i].mousePressEvent = self.print_some
def print_some(self, event):
print("Clicked")
But I didn't figure out which object clicked . How can i do that ?
You can easily make custom receivers for events, which would contain the event source information:
import functools
labels[i].mousePressEvent = functools.partial(self.print_some, source_object=labels[i])
def print_some(self, event, source_object=None):
print("Clicked, from", source_object)

PySide how to tell if a push button has been clicked?

I have a layout with 5 buttons which I act as "menus", so you click on one button and one view will show up, you click another button and another view shows up. I need to find out which button is clicked so I can do something based on which button is pressed. Something like
if button1_is_clicked:
do_something()
else:
do_something_else()
What would be the best way to approach this?
Here is my code:
I want to be able to change the stylesheet of the button, so an active state and a non-active state
from PySide import QtCore
from PySide import QtGui
import VulcanGui
#--------------------------------------------------------------------------
class Program(QtGui.QMainWindow, VulcanGui.Ui_MainWindow):
def __init__(self, parent=None):
""" Initialize and setup the User Interface """
super(Program, self).__init__(parent)
self.setupUi(self)
""" Populate the Main Area """
self.mainArea.setHtml(self.intro_text())
""" Button Signal/Slots """
self.introButton.toggled.connect(self.intro_area)
self.runVulcanButton.clicked.connect(self.vulcan_run_area)
self.vulcanLogButton.clicked.connect(self.vulcan_log_area)
self.hostFileButton.clicked.connect(self.edit_host_area)
self.configEditButton.clicked.connect(self.edit_config_area)
def intro_text(self):
content_file = open("../content/intro_text.html").read()
return content_file
'''
Get the content to print
'''
def intro_area(self):
content_file = open("../content/intro_text.html").read()
self.mainArea.setHtml(content_file)
'''
Function that will display the data when the 'Run Vulcan' button is pressed
'''
def vulcan_run_area(self):
self.mainArea.setPlainText("Button Two ")
'''
Function that will display the data when the 'Vulcan Log' button is pressed
'''
def vulcan_log_area(self):
self.mainArea.setPlainText("Button Three")
'''
Function that will display the data when the 'Edit Host File' button is pressed
'''
def edit_host_area(self):
self.mainArea.setPlainText("Button Four")
'''
Function that will display the data when the 'Edit Config File' button is pressed
'''
def edit_config_area(self):
self.mainArea.setPlainText("Button Five")
#--------------------------------------------------------------------------
if __name__ == "__main__":
import sys
program = QtGui.QApplication(sys.argv)
mWindow = Program()
mWindow.show()
sys.exit(program.exec_())
I suggest you learn the basics of Qt to get acquainted with signals and slots.
You need to make the initially visible QPushButtons checkable (otherwise the 'revealed' buttons will only appear whilst the button is held down), and connect the toggled(bool) signal to the setVisible(bool) slot of the buttons you want to 'reveal'. Obviously for the buttons that are initially invisible, you would have to call setVisible(false) upon instantiation.
There are other, more reusable, ways of achieving the same effect - but this will get you started.

Categories