Is there a mouseEntered / mouseExited signal in Qt? (Equivalent to mouseEntered / mouseExited from Java) I can't seem to find it.
You will have to subclass your widget and install an eventFilter onto it.
class MyButton(QPushButton):
def __init__(self):
super(MyButton, self).__init__()
self.show()
self.installEventFilter(self) //Install event filter on widget
def eventFilter(self, object, event): //Define eventFilter method
if event.type() == Event.Enter:
print(“Mouse is hovering over button”)
elif event.type() == QEvent.Leave:
print(“Mouse has left the button”)
return False
Related
I want to set the background in dim mode, when a QMessagebox is popped up.
Currently, I have tried to use a simple QMesssagebox, but the background shows as normal display, when it pops up.
The image for 1st page is as follow
When go to next slide is pushed, it goes to next index as follow
When going back to 1st index, the back button is pushed which pops up the messagebox as follow
However, the mainwindow seems to have no effect on its focus.
Therefore, what would I need to do to make it dimmer than the focused messagebox.
How can I do this? Any suggestions?
EDIT
import sys
from PyQt5 import uic
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.ui = uic.loadUi("message.ui",self)
self.notification = QMessageBox()
self.ui.next_slide.clicked.connect(self.second_index)
self.ui.go_back.clicked.connect(self.alert_msg)
self.show()
def home(self):
self.ui.stackedWidget.setCurrentIndex(0)
def second_index(self):
self.ui.stackedWidget.setCurrentIndex(1)
def alert_msg(self):
self.notification.setWindowTitle("Exiting")
self.notification.setText("Are you sure, you want to exit")
self.notification.setIcon(QMessageBox.Critical)
self.notification.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
self.back = self.notification.exec_()
if self.back == QMessageBox.Yes:
self.home()
else:
pass
if __name__ == "__main__":
app=QApplication(sys.argv)
mainwindow=MainWindow()
app.exec_()
You can create a custom widget that is a direct child of the window that has to be "dimmed", ensure that it always has the same size as that window, and just paint it with the selected color:
class Dimmer(QWidget):
def __init__(self, parent):
parent = parent.window()
super().__init__(parent)
parent.installEventFilter(self)
self.setAttribute(Qt.WA_DeleteOnClose)
self.adaptToParent()
self.show()
def adaptToParent(self):
self.setGeometry(self.parent().rect())
def eventFilter(self, obj, event):
if event.type() == event.Resize:
self.adaptToParent()
return super().eventFilter(obj, event)
def paintEvent(self, event):
qp = QPainter(self)
qp.fillRect(self.rect(), QColor(127, 127, 127, 127))
class MainWindow(QMainWindow):
# ...
def alert_msg(self):
dimmer = Dimmer(self)
# ...
self.back = self.notification.exec_()
dimmer.close()
Note that, unless you plan to reuse the "dim widget", it must be destroyed either by calling close() as done above (see the WA_DeleteOnClose flag) or using deleteLater(). Hiding it will not be enough.
Example:
class tab_1(QWidget):
def __init__(self):
super(tab_1, self).__init__()
self.initUI()
def initUI(self):
self.btn = QPushButton(self)
self.btn.setText("tab1")
self.btn.setWhatsThis(
"""''(parameter) self: ~tab
Sourcery Code Metrics
Complexity 0 ⭐
Size 108 🙂
Working Memory 4 ⭐
Quality Score 82 % ⭐
⟠ self: [tab] Docc"""
""
)
I use the setWhatsThis and try to copy/select the text of it, but
when I click the mouse
In WhatsThis area is Disappear!
Example
In general, the what's this behavior is to only handles clicks on hyperlinks (which normally launch the application help dialog), so if the user clicks it, it will automatically close anyway.
The only solution is to override the default behavior by mimicking what Qt normally does: install an event filter on the application whenever a top level window receives a EnterWhatsThisMode event type, and create a custom widget that is shown whenever it's appropriate.
Since this will cause to override all what's this cases, I'd suggest to also set the WA_CustomWhatsThis attribute for the widgets that will have selectable text, so that you can use the standard behavior for any other case.
The custom widget is actually a QLabel with the Popup window flag set, which will make the widget a top level window that also captures mouse events, and with the TextSelectableByMouse and LinksAccessibleByMouse text interaction flags set.
class SelectableWhatsThis(QtWidgets.QLabel):
def __init__(self, parent, pos):
super().__init__(parent.whatsThis(), parent, flags=QtCore.Qt.Popup)
self.setTextInteractionFlags(
QtCore.Qt.TextSelectableByMouse | QtCore.Qt.LinksAccessibleByMouse)
self.setBackgroundRole(QtGui.QPalette.ToolTipBase)
self.setForegroundRole(QtGui.QPalette.ToolTipText)
self.setAutoFillBackground(True)
self.setContentsMargins(12, 8, 12, 8)
self.move(pos)
self.show()
def mousePressEvent(self, event):
if event.pos() not in self.rect():
self.close()
else:
super().mousePressEvent(event)
def keyPressEvent(self, event):
self.close()
class WhatsThisHelper(QtCore.QObject):
installed = active = False
whatsThis = None
def __init__(self, parent):
super().__init__(parent)
if not self.installed:
# ensure that only one instance of WhatsThisHelper is installed
self.__class__.installed = True
QtWidgets.QApplication.instance().installEventFilter(self)
self.active = True
def eventFilter(self, obj, event):
if not obj.isWidgetType():
return False
if (event.type() == event.MouseButtonPress
and event.button() == QtCore.Qt.LeftButton
and obj.whatsThis() and
obj.testAttribute(QtCore.Qt.WA_CustomWhatsThis)):
self.whatsThis = SelectableWhatsThis(obj, event.globalPos())
QtWidgets.QApplication.restoreOverrideCursor()
return True
elif event.type() in (event.MouseButtonRelease, event.MouseButtonDblClick):
if self.whatsThis:
QtWidgets.QWhatsThis.leaveWhatsThisMode()
return True
elif event.type() == event.LeaveWhatsThisMode:
QtWidgets.QApplication.instance().removeEventFilter(self)
self.deleteLater()
return super().eventFilter(obj, event)
def __del__(self):
if self.active:
self.__class__.installed = False
class MainWindow(QtWidgets.QMainWindow):
# ...
def event(self, event):
if event.type() == event.EnterWhatsThisMode:
WhatsThisHelper(self)
return super().event(event)
Note that you need to override the event of all widgets that are going to be top level widgets (all QMainWindows, QDialogs, etc) and need to support this.
I'm having an issue where I have implemented a dragging feature for widgets using eventFilter(), but it seems when I drag towards the right and my cursor hovers over the widget which in my case is a QPushButton it seems to stop tracking until I hover out of the widget
How would I fix this?
class widget1(QWidget):
def __init__(self):
super().__init__()
self.widget = QPushButton("button0", self)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.createbutton = QPushButton('+', self)
self.createbutton.setGeometry(5, 5, 15, 15)
self.createbutton.clicked.connect(self.createWidget)
self.show()
def createWidget(self):
self.new_widget = widget1()
self.new_widget.setParent(self)
self.new_widget.show()
self.new_widget.installEventFilter(self)
def eventFilter(self, source, event):
if event.type() == QEvent.MouseMove:
MousePos = QPoint(event.pos())
if event.buttons() == Qt.LeftButton:
source.move(source.x() + MousePos.x(), source.y() + MousePos.y())
return super(MainWindow, self).eventFilter(source, event)
Basically I have a class with a widget in it called widget1() (This will be my custom widget in the future) I am then adding it dynamically to the window every time I press self.createbutton using .show() instead of using a layout, then it installs the event filter to it so that it can allow for dragging.
The event filter is receiving events for the widget1 instance but not for its child widgets (i.e. the QPushButton). A quick fix is to invoke QMouseEvent.ignore for the QPushButton's mouseMoveEvent so the mouse event will be propagated up to the parent widget and received in the event filter.
class widget1(QWidget):
def __init__(self):
super().__init__()
self.widget = QPushButton("button0", self)
self.widget.mouseMoveEvent = lambda event: event.ignore()
I'm writing Chat gui for client on Python using PyQt5.
I have a QTextEdit, which the client can write messages in it.
I wan't to know when the 'Enter' key is being pressed while the focus is on the QTextEdit.
I tried using installEventFilter function but it detects keys being pressed on all of the other widgets but the QTextEdit one.
What can I do to fix that?
def initUI(self):
# ...
self.text_box = QtWidgets.QTextEdit(self)
self.installEventFilter(self)
# ...
def keyPressEvent(self, qKeyEvent):
print(qKeyEvent.key())
if qKeyEvent.key() == Qt.Key_Return:
if self.text_box.hasFocus():
print('Enter pressed')
When you override keyPressEvent you are listening to the events of the window, instead install an eventFilter to the QTextEdit, not to the window as you have done in your code, and check if the object passed as an argument is the QTextEdit:
def initUI(self):
# ...
self.text_box = QtWidgets.QTextEdit(self)
self.text_box.installEventFilter(self)
# ...
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.KeyPress and obj is self.text_box:
if event.key() == QtCore.Qt.Key_Return and self.text_box.hasFocus():
print('Enter pressed')
return super().eventFilter(obj, event)
The answer from #eyllanesc is very good if you are determined to use QTextEdit.
If you can get away with QLineEdit and its limitations, you can use the returnPressed() signal. The biggest drawback for QLineEdit is you are limited to one line of text. And there is no word wrap. But the advantage is you don't have to mess with eventFilters or think too hard about how keyPress signals fall through all of the widgets in your app.
Here is a minimal example that copies from one QLineEdit to another:
import sys
from PyQt5.QtWidgets import *
class PrintWindow(QMainWindow):
def __init__(self):
super().__init__()
self.left=50
self.top=50
self.width=300
self.height=300
self.initUI()
def initUI(self):
self.setGeometry(self.left,self.top,self.width,self.height)
self.line_edit1 = QLineEdit(self)
self.line_edit1.move(50, 50)
self.line_edit1.returnPressed.connect(self.on_line_edit1_returnPressed)
self.line_edit2 = QLineEdit(self)
self.line_edit2.move(50, 100)
self.show()
def on_line_edit1_returnPressed(self):
self.line_edit2.setText(self.line_edit1.text())
if __name__ == '__main__':
app = QApplication(sys.argv)
window = PrintWindow()
sys.exit(app.exec_())
In this example, I have manually connected to the signal in line 22 (self.line_edit1.returnPressed.connect). If you are using a ui file, this connection can be left out and your program will automatically call the on__returnPressed method.
When you override keyPressEvent you are listening to the events of the window, instead install an eventFilter to the QTextEdit, not to the window as you have done in your code, and check if the object passed as an argument is the QTextEdit:
def initUI(self):
# ...
self.text_box = QtWidgets.QTextEdit(self)
self.text_box.installEventFilter(self)
# ...
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.KeyPress and obj is self.text_box:
if event.key() == QtCore.Qt.Key_Return and self.text_box.hasFocus():
print('Enter pressed')
return True
return False
This is building upon the answer of #eyllanesc and the problem #Daniel Segal faced. Adding the correct return values as such to the eventFilter solves the problem.
I am catching key press events by overriding the KeyPressEvent method of my class, inheriting from QWidget, but I cannot see key press events for the ESCAPE and BACKSPACE keys, as well as C-x or again C-c. I can see key release events though. Who catches them ?
class KeyCaptureWidget(QWidget):
# init…
def keyPressEvent(self, event):
key = event.key()
logging.info("key press: {}".format(key))
def keyReleaseEvent(self, event):
key_code = event.key()
logging.info("key release: {}".format(key_code))
The difference between C-x and say C-b is the following:
pressing C-b prints: control is pressed, b is pressed, b is released, control is released.
pressing C-x prints: control is pressed, [nothing], x is released, control is released.
In my QWidget, I use a QVBoxLayout to which I put two QWebEngine views. I tried to override the key press method of the QWebEngineView, but they don't seem to catch anything (and this is the behaviour I expected).
def __init__(self):
self.qtwindow = KeyCaptureWidget()
self.layout = QVBoxLayout()
self.view1 = QWebEngineView() # or a custom one to override keyPressEvent
# self.view2 = the same
self.layout.addWidget(self.view1)
self.layout.addWidget(self.view2)
self.qtwindow.setLayout(self.layout)
self.qtwindow.show()
I tried to catch these with event filters on my QWidget class but again, no luck: I see only a key release for ESC or BACKSPACE.
def __init__(self):
super().__init__()
self.installEventFilter(self)
def eventFilter(self, source, event):
logging.info("event filter: {}".format(event))
if event.type() == QEvent.KeyPress:
logging.info(" event filter key PRESS")
if event.type() == QEvent.KeyRelease:
logging.info(" event filter key RELEASE")
return True
How can I catch them ? Thank you.
Events do not necessarily propagate among all widgets, if a widget consumes it then it will no longer propagate to the parent. In the case of the keyboard events will only be consumed first by the widget that has the focus, in your case QWebEngineView consumes them before and prevents it from being projected in other widgets. If you want to hear events from the keyboard of a window then you must use the QShortcuts, and for that you must create a QShortcut:
Ctrl + C: QtGui.QKeySequence("Ctrl+C")
Ctrl + X: QtGui.QKeySequence("Ctrl+X")
Esc: QtGui.QKeySequence("Escape")
Backspace: QtGui.QKeySequence("Backspace")
Considering the above, the solution is:
from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.m_view1 = QtWebEngineWidgets.QWebEngineView()
self.m_view2 = QtWebEngineWidgets.QWebEngineView()
self.m_view1.load(QtCore.QUrl("https://stackoverflow.com/questions/56890831"))
self.m_view2.load(QtCore.QUrl("https://doc.qt.io/"))
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.m_view1)
lay.addWidget(self.m_view2)
QtWidgets.QShortcut(
QtGui.QKeySequence("Ctrl+C"), self, activated=self.on_Ctrl_C
)
QtWidgets.QShortcut(
QtGui.QKeySequence("Ctrl+X"), self, activated=self.on_Ctrl_X
)
QtWidgets.QShortcut(
QtGui.QKeySequence("Escape"), self, activated=self.on_Escape
)
QtWidgets.QShortcut(
QtGui.QKeySequence("Backspace"), self, activated=self.on_Backspace
)
#QtCore.pyqtSlot()
def on_Ctrl_C(self):
print("Ctrl+C")
#QtCore.pyqtSlot()
def on_Ctrl_X(self):
print("Ctrl+X")
#QtCore.pyqtSlot()
def on_Escape(self):
print("Escape")
#QtCore.pyqtSlot()
def on_Backspace(self):
print("Backspace")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())