Is it possible to trigger a mousePressEvent artificially on a QWebView? - python

Due to an astronomically atrocious bug on PyQt4, I need to fire a mousePressEvent artificially on a QWebView as a last breath of hope. For some obscure reason, QWebView's linkClicked and urlChanged signals do not pass a QUrl when the clicked link has Javascript involved (It does not work on Youtube videos, for example) and when the link was clicked with the left mouse button. The QUrl can be perfectly accessed when the link was clicked with the middle and right buttons.
class CQWebView(QtWebKit.QWebView):
def mousePressEvent(self, event):
if type(event) == QtGui.QMouseEvent:
if event.button() == QtCore.Qt.LeftButton:
# FIRE A QtCore.Qt.MiddleButton EVENT HERE,
# SO THAT I CAN GET THE BLOODY LINK AFTERWARDS.
elif event.button() == QtCore.Qt.MiddleButton:
self.emit(QtCore.SIGNAL("OPEN_IN_NEW_TAB")) # Example
elif event.button() == QtCore.Qt.RightButton:
self.emit(QtCore.SIGNAL("XXX")) # Example
So, I literally want to "click artificially", click without clicking, just trigger the event of clicking with the middle right button on a link, so that I can catch the QUrl correctly.

You can do it using the QtTest module as well. Set an event filter on your web view and check for left click mouse events and send a middle mouse click
def __init__(...)
self.ui_web_view.installEventFilter(self)
def eventFilter(self, obj, event):
if obj == self.ui_web_view:
if event.type() == QtCore.QEvent.MouseButtonPress:
if event.button() == QtCore.Qt.LeftButton:
print 'Handled'
QtTest.QTest.mouseClick(self.ui_web_view, QtCore.Qt.MiddleButton, QtCore.Qt.NoModifier, event.pos())
return True
return False

I've managed to click with the middle button by clicking with the left button using the sendEvent method from QtGui.QApplication.
class CQWebView(QtWebKit.QWebView):
app = None
def __init__(self, app):
QtWebKit.QWebView.__init__(self)
CQWebView.app = app
def mousePressEvent(self, event):
if type(event) == QtGui.QMouseEvent:
if event.button() == QtCore.Qt.LeftButton:
CQWebView.app.sendEvent(self, QtGui.QMouseEvent(event.type(), event.pos(), QtCore.Qt.MiddleButton, event.buttons(), event.modifiers()))
elif event.button() == QtCore.Qt.MiddleButton:
self.emit(QtCore.SIGNAL("linkClicked(const QUrl&)")) # Problem
elif event.button() == QtCore.Qt.RightButton:
self.emit(QtCore.SIGNAL("XXX")) # Example
I just needed to pass the instance of the QApplication to the instance of the custom QWebView on instantiation:
def compose_tab(self, index):
self.tabs[index].append(CQWebView(self))
This way, I click on the QWebView with the left mouse button and I click artificially on it with the middle button. The problem is that doing so I'm overriding the default behavior of the linkClicked and urlChanged signals from the QWebView. Even though I can emit these signals, I can't have access to the address of the link that was clicked, since by accessing QWebView.url() only gives me the present QUrl, and not the future clicked one.
Really frustrating.

Related

Get mouse press event from ANY widget

I have a QMainWindow application that has multiple widgets (buttons, labels, etc.) inside it.
How can I get an event when the user presses ANYWHERE of the app?
I tried to customize mousePressEvent() function, but this doesn't accept the event when other widgets (buttons, labels, etc.) are pressed.
Explanation:
The handling of mouse events between the widgets goes from children to parents, that is, if the child does not accept the event (it does not use it) then it will pass the event to the parent. For example, if you press on the QPushButton, it accepts the event and the parent is not notified, unlike QLabel that does not consume it, so the event passes to the parent.
Tools:
On the other hand, there are several methods to listen to events in general, such as:
Override any method like mousePressEvent, keyPressEvent, etc or the event or customEvent method.
Use an eventFilter.
Solution:
An alternative is to apply some previous method to all the widgets but you can discard all of them for the current objective, for example:
In the first and second method it would involve detecting when a widget is added or removed (using QEvent::ChildAdded or QEvent::ChildRemoved).
In the first method it would imply override the methods that many times is impossible.
With the above, the problem is attacked on the widgets side, but there are other alternatives:
Override the notify() method of Q{Core, GUi,}Application, verify that it is a widget and that it belongs to the window, it also implies discriminating if the event has already been consumed.
Listen to the mouse event associated with the window (QWindow).
In this case the most reasonable is the second method.
import sys
from PyQt5 import QtCore, QtWidgets
class MouseObserver(QtCore.QObject):
pressed = QtCore.pyqtSignal(QtCore.QPoint)
released = QtCore.pyqtSignal(QtCore.QPoint)
moved = QtCore.pyqtSignal(QtCore.QPoint)
def __init__(self, window):
super().__init__(window)
self._window = window
self.window.installEventFilter(self)
#property
def window(self):
return self._window
def eventFilter(self, obj, event):
if self.window is obj:
if event.type() == QtCore.QEvent.MouseButtonPress:
self.pressed.emit(event.pos())
elif event.type() == QtCore.QEvent.MouseMove:
self.moved.emit(event.pos())
elif event.type() == QtCore.QEvent.MouseButtonRelease:
self.released.emit(event.pos())
return super().eventFilter(obj, event)
class MainWindow(QtWidgets.QMainWindow):
pass
def main(args):
app = QtWidgets.QApplication(args)
w = MainWindow()
w.show()
mouse_observer = MouseObserver(w.window().windowHandle())
mouse_observer.pressed.connect(lambda pos: print(f"pressed: {pos}"))
mouse_observer.released.connect(lambda pos: print(f"released: {pos}"))
mouse_observer.moved.connect(lambda pos: print(f"moved: {pos}"))
app.exec_()
if __name__ == "__main__":
main(sys.argv)

Capture all key-presses in PyQt

I am trying to capture all of key pressed in my program
def keyPressEvent(self, event):
event = event.key()
if (event == QtCore.Qt.Key_Q):
print ('Q!.')
That function works fine when i am trying to capture keys in my window. (in this case Q_Key)
But if the key was pressed in a Text Widget (for example in a: QListView, QlistWidget, QLineEdit, and so many more ) it don't work. The function prints nothing. I am doing something wrong...
What can i do to fix it?
You will need to install an event-filter on the application object to get all key-press events:
class Window(QMainWindow):
def __init__(self):
...
QApplication.instance().installEventFilter(self)
def eventFilter(self, source, event):
if event.type() == QtCore.QEvent.KeyPress:
print('KeyPress: %s [%r]' % (event.key(), source))
return super().eventFilter(source, event)

Problems handling QTextEdit event()

I have developed a gui in Qt Designer, that among other things, displays a QTextEdit widget.
I need to handle the user entering the certain key combinations (TAB key, SHIFT + ENTER and SHIFT + RETURN so far) in such a way that it doesn't display those characters in the QTextEdit widget and implements some custom functionality.
I have created a handler to detect keyReleaseEvents, but that seems to be too late to prevent tabs, carriage returns etc. getting displayed, so then I tried handling keyPressEvents, but that doesn't seem to work at all. From what I've read online, people are saying that the keyPressEvent is getting consumed by the widget and so never gets propagated up to the gui. I don't know enough about QTextEdit widgets to say if that's correct, but for now I'm assuming it is.
So then I tried this in the class __init__ method for the gui (I have left out the check for the RETURN key to keep the code short below:
self.textEdit.event = self.eventX
Here's the code for the eventX method:
def eventX(self, event):
if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Tab:
print 'CODE WINDOW: Tab pressed'
self.textEdit.insertPlainText(QString('| |'))
self.textEdit.update()
event.accept()
return True
elif event.type() == QEvent.KeyPress and (event.key() == Qt.Key_Enter) and event.modifiers() & Qt.ShiftModifier:
print 'CODE WINDOW: Shift pressed with Enter'
self.submitCode()
self.textEdit.update()
event.accept()
return True
else:
event.ignore()
return QWidget.event(self, event)
This is where things get strange. The code above will sort of work if I have specified that:
verticalScrollBarPolicy = Qt.ScrollBarAsNeeded
for the QTextEdit widget, except that it won't actually display the characters in the widget, but it does seem to handle the TAB and SHIFT-ENTER/SHIFT-RETURN keys being pressed?
And then, if I set verticalScrollBarPolicy = Qt.ScrollBarAlwaysOn, the eventX method seems to get completely ignored!
This is driving me crazy at this stage. Does anyone know what I'm doing wrong?
Thanks.
UPDATE: Based on the information from MDURANT, I now have the code below that works for me:
def handleEditorKeyPress(self, event):
if event.key() == Qt.Key_Tab:
# My custom code goes here.
event.accept()
return
elif event.key() == Qt.Key_Enter and event.modifiers() & Qt.ShiftModifier:
# My custom code goes here.
event.accept()
return
elif event.key() == Qt.Key_Return and event.modifiers() & Qt.ShiftModifier:
# My custom code goes here.
event.accept()
return
else:
event.ignore()
return QTextEdit.keyPressEvent(self.textEdit, event)
An example of capturing key events. In this case, I simply pass the event through to another identical widget, but in general, you can set the event function to anything
class win(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self, parent=None)
self.box = QtGui.QHBoxLayout(self)
self.setLayout(self.box)
self.ed1 = QtGui.QTextEdit(self)
self.ed2 = QtGui.QTextEdit(self)
self.ed1.keyPressEvent = self.ed2.keyPressEvent
self.ed1.keyReleaseEvent = self.ed2.keyReleaseEvent
self.box.addWidget(self.ed1)
self.box.addWidget(self.ed2)
See the documentation of the event: http://qt-project.org/doc/qt-4.8/qtextedit.html#keyPressEvent

Pyqt Mouse hovering on a QPushButton

I want to detect the hovering of the mouse on a QPushButton. For that I installed an event filter on my button. However the MouseMove event does not trigger exactly when the mouse is over the button. It seems it is sometimes triggered when I click the button on a location which is different from the previous one. To put it simply:
I move the mouse on the button: nothing happens.
I click: MouseButtonPressed event is triggered.
I move the mouse to another location on the button: nothing happens.
I click again: MouseButtonPressed is triggered, MouseMove too.
I would like to get the MouseMove triggered each time the mouse hovers the button. How do I do?
Here is my code:
import sys
from PyQt4 import QtCore
from PyQt4.QtGui import *
class eventFilterWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
widget = QWidget()
button = QPushButton("Trigger event!")
button.installEventFilter(self)
hbox = QHBoxLayout()
hbox.addWidget(button)
widget.setLayout(hbox)
self.setCentralWidget(widget)
self.show()
def eventFilter(self, object, event):
if event.type() == QtCore.QEvent.MouseButtonPress:
print "You pressed the button"
return True
elif event.type() == QtCore.QEvent.MouseMove:
print "C'mon! CLick-meeee!!!"
return True
return False
def main():
app = QApplication(sys.argv)
#myWindow = EventsWindow()
window = eventFilterWindow()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
EDIT:
In fact, MouseMove is triggered when the mouse is being moved while the QPushButton is pressed.
I found the answer. I was misled as I was searching an event containing the keyword Mouse in it. The event I was looking for actually is QtCore.QEvent.HoverMove.

PyQt4 MouseMove event without MousePress

I need to catch when a User moves the mouse over the GUI, but not when they're holding down the mouse button (which would do something different).
I can't find any conveniant method to do this,
except to periodically find the mouse position and check it to it's previous position...
Which would suck.
The mouseMoveEvent is only called when the mouse is moved whilst the left mouse button is pressed,
unless ofcourse the widget has 'mouse tracking'. Mouse tracking is not an option for me, because the GUI must behave differently when the mouse is moved and the left mouse button is pressed.
Are there any inbuilt methods to do this?
(or just any clever ideas?)
eg:
Is there a way to check if the left mouse button is being pressed at any time?
Or a 'mouse hover' event that can be applied to a QRect (coordinates)?
Muchas gracias.
Windows 7 (32)
python 2.7
PyQt4
The most straightforward way to do this is to install an event filter on qApp:
from PyQt4 import QtGui, QtCore
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
widget = QtGui.QWidget(self)
layout = QtGui.QVBoxLayout(widget)
self.edit = QtGui.QLineEdit(self)
self.list = QtGui.QListWidget(self)
layout.addWidget(self.edit)
layout.addWidget(self.list)
self.setCentralWidget(widget)
def eventFilter(self, source, event):
if event.type() == QtCore.QEvent.MouseMove:
if event.buttons() == QtCore.Qt.NoButton:
pos = event.pos()
self.edit.setText('x: %d, y: %d' % (pos.x(), pos.y()))
else:
pass # do other stuff
return QtGui.QMainWindow.eventFilter(self, source, event)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
win = Window()
win.show()
app.installEventFilter(win)
sys.exit(app.exec_())
As people have said, the correct approach seems to be to call setMouseTracking(True) on the widget. What I'd like to add is that, having done this, you can distinguish between a mouse motion and a mouse drag as follows:
def mouseMoveEvent(self, event):
if event.buttons() == QtCore.Qt.NoButton:
print "Simple mouse motion"
elif event.buttons() == QtCore.Qt.LeftButton:
print "Left click drag"
elif event.buttons() == QtCore.Qt.RightButton:
print "Right click drag"
call setMouseTracking(True) method first. Then mouseMoveEvent will be fired without any button pressed.
It seems you've misunderstood what mouseTracking does. It only causes mouseMoveEvent to be fired, nothing else. In other words, it's exactly what you need.
Check the event's buttons() to see if any button was pressed:
For mouse move events, this is all buttons that are pressed down.

Categories

Resources