I would like to recognize a single mouse click over a QWidget in PyQt5. In the docs there is either mouseDoubleClickEvent or mousePressEvent but there is no mouseClickEvent function for a single click. How do I get that functionality? Thank you
BTW I've noticed PyQtGraph does have a mouseClickEvent function.
Qt doesn't provide a "click" event on its own, you have to implement such a feature based on mousePressEvent and mouseReleaseEvent, by checking that the release has happened within the geometry of the widget (and, possibly, that the correct mouse button was pressed and released).
class ClickWidget(QWidget):
pressPos = None
clicked = pyqtSignal()
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.pressPos = event.pos()
def mouseReleaseEvent(self, event):
# ensure that the left button was pressed *and* released within the
# geometry of the widget; if so, emit the signal;
if (self.pressPos is not None and
event.button() == Qt.LeftButton and
event.pos() in self.rect()):
self.clicked.emit()
self.pressPos = None
Related
I'm trying to make volume button, on click it should mute/unmute and and on hover it should popup QSlider, so user can set whatever level he wants. Now I'm trying to achieve this by showing slider window in enterEvent and hiding it in leaveEvent:
class VolumeButton(QToolButton):
def __init__(self, parent=None):
super().__init__(parent)
self.setIcon(volumeicon)
self.slider = QSlider()
self.slider.setWindowFlags(Qt.FramelessWindowHint)
self.slider.setWindowModality(Qt.NonModal)
def enterEvent(self, event):
self.slider.move(self.mapToGlobal(self.rect().topLeft()))
self.slider.show()
def leaveEvent(self, event):
self.slider.hide()
The problem is that mapToGlobal seems to be connected in some way with enterEvent and it creates recursion, but without mapToGlobal I can't place slider at the right position.
I'm not sure that QToolButton and FramelessWindow are the right widgets to achieve wished result, so let me know if there a better ways to do that.
The problem is not from mapToGlobal, but from the fact that the leaveEvent is fired as soon as the slider is shown: since the slider is in the same coordinates of the mouse, Qt considers that the mouse has "left" the button (and "entered" the slider).
You cannot use the simple leaveEvent for this, as you need to check the cursor position against both the button and the slider.
A possible solution is to create a QRegion that contains the geometry of both widgets and check if the cursor is inside it. In order to process the mouse events of the slider, an event filter must be installed on it:
class VolumeButton(QToolButton):
def __init__(self, parent=None):
super().__init__(parent)
self.setIcon(volumeicon)
self.slider = QSlider()
self.slider.setWindowFlags(Qt.FramelessWindowHint)
self.slider.setWindowModality(Qt.NonModal)
self.slider.installEventFilter(self)
def isInside(self):
buttonRect = self.rect().translated(self.mapToGlobal(QPoint()))
if not self.slider.isVisible():
return QCursor.pos() in buttonRect
region = QRegion(buttonRect)
region |= QRegion(self.slider.geometry())
return region.contains(QCursor.pos())
def enterEvent(self, event):
if not self.slider.isVisible():
self.slider.move(self.mapToGlobal(QPoint()))
self.slider.show()
def leaveEvent(self, event):
if not self.isInside():
self.slider.hide()
def eventFilter(self, source, event):
if source == self.slider and event.type() == event.Leave:
if not self.isInside():
self.slider.hide()
return super().eventFilter(source, event)
Note that self.rect() is always at coordinates (0, 0), so you can just use self.mapToGlobal(QPoint()) to get the widget's global position. On the other hand, you might want to show the slider "outside" the button, so you could use self.mapToGlobal(self.rect().bottomLeft()).
Be aware that trying to place the slider "outside" the button geometry might result in a problem if the user moves the mouse in the gap between the button and the slider; in that case, you will need to create a valid region that covers both widgets and a reasonable space between them.
Questions and Briefing
Hello. I am trying to fix a problem with my PyQt5 window which has a custom Title Bar. If you maximize the window and you hover over the title bar with the cursor and drag the window to move it, the window will resize and you will be able to move it as normal.
However, when the window restores previous state before it is maximized using the cursor click and drag method, the cursor does not grip onto the location on the title bar to move the window, but, it stays in the same place where the click and drag method was started so the cursor is on the other side of the screen mysteriously moving the window.
How would I make it so the cursor would grip onto the title bar and when the window is restored using the click and drag method it would be locked on to that same location, the same as you would have for normal applications.
Sub-Questions
How would I get the location of the cursor inside the window? I have managed to get the cursor location using QCursor.pos() but that returns the global position on the actual screen but I think I need it on the window so I can try to make it so the cursor grips onto the title bar.
I have tried to...
Use QCursor.pos() before the click and drag restoration method is done and then I did QCursor.setPos() to set the previously captured position, it does work but then it causes the window to move with it and the cursor is also out of position from the intended grip location inside the window.
Code
class MainWindow(QMainWindow): # Main Window
def __init__(self): # Main initialisation
super(MainWindow, self).__init__() # Call superclass
# UI Setup
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# Move Window
def move_window(event):
# Restore Window (before movement)
if UIFunctions.return_status(self) == 1: # If maximized
UIFunctions.maximize_restore(self)
# QCursor.setPos(self.previous_cursor_pos)
if event.buttons() == Qt.LeftButton: # If Left Click (move window)
self.move(self.pos() + event.globalPos() - self.dragPos)
self.dragPos = event.globalPos()
event.accept()
# Set Title Bar
self.ui.title_bar.mouseMoveEvent = move_window
# Set UI Definitions
UIFunctions.ui_definitions(self)
self.show() # Show main window
def mousePressEvent(self, event): # Mouse Press Event
self.dragPos = event.globalPos()
(Main Class with Move Window method)
Other Info
Python Version: Python 3.8.6
Module Used: PySide2
Happy to provide more information.
musicmante's solution in the comments fixed my issue. I was trying to use something like self.pos.x() and after I switched it to self.mapFromGlobal(QCursor.pos()).x()
In the mouseMoveEvent method I've seen code like below to check if either the left or right mouse buttons are being pushed (as shown below). Is there a way to check if certain keyboard keys are currently being pressed? Ideally I would like to have the action performed when the mouse is leftclicked and moved and a certain key is being pushed. I don't see a way to use keyPressEvent as this is called separately from the mouseMoveEvent (just like mousePressEvent is not used in this example).
def mouseMoveEvent(self, event):
if event.buttons() & QtCore.Qt.LeftButton:
#run this when mouse is moved with left button clicked
elif event.buttons() & QtCore.Qt.RightButton:
#run this when mouse is moved with right button clicked
Edit: Based on ekhumoro's comment method now looks like this. It only works for key modifiers:
def mouseMoveEvent(self, event):
modifiers = QtGui.QApplication.keyboardModifiers()
if bool(event.buttons() & QtCore.Qt.LeftButton) and (bool(modifiers == QtCore.Qt.ControlModifier)):
#run this when mouse is moved with left button and ctrl clicked
elif event.buttons() & QtCore.Qt.LeftButton:
#run this when mouse is moved with left button clicked
elif event.buttons() & QtCore.Qt.RightButton:
#run this when mouse is moved with Right button clicked
If anyone is able to get this to work with any key a response would be greatly appreciated
def mouseMoveEvent(self, event):
modifiers = QApplication.keyboardModifiers()
Mmodo = QApplication.mouseButtons()
if bool(Mmodo == QtCore.Qt.LeftButton) and (bool(modifiers == QtCore.Qt.ControlModifier)):
print 'yup'
I have a QGraphicsScene and many selectable items. But when I click the right mouse button - deselects all objects. I want show menu and edit selected objects but have automatic deselect any time when right click at mouse...
Perhaps the problem is that I have included an rubber selection. selection of objects in the end is how the right and the left mouse button when I pull the frame and therefore is reset at single time you press the right button...
How to leave objects highlighted when you click on the right mouse button? Or it may be necessary to disable the rubber selection of the right button?
Daniele Pantaleone answer gave me an idea and I have modified the function of mousePressEvent() and immediately got the desired effect me
def mousePressEvent(self, event):
if event.button() == Qt.MidButton:
self.__prevMousePos = event.pos()
elif event.button() == Qt.RightButton: # <--- add this
print('right')
else:
super(MyView, self).mousePressEvent(event)
A possible solution would be to use mouseReleaseEvent to display the contextual menu instead of contextMenuEvent:
def mouseReleaseEvent(self, mouseEvent):
if mouseEvent.button() == Qt.RightButton:
# here you do not call super hence the selection won't be cleared
menu = QMenu()
menu.exec_(mouseEvent.screenPos())
else:
super().mouseReleaseEvent(mouseEvent)
I haven't been able to test it but I guess it should work. The point is that the selection is cleared by default by QGraphicsScene, so what you need to do is to prevent the clearing from happening when certain conditions are met, in your case when the contextual menu needs to be displayed.
I can't seem to get any mouse clicks in a QTreeWidget. I have tried...
...overriding mousePressEvent, but it never runs at all. Not even to log a message.
...using an event filer. It works for everything but mouse clicks.
...using delegates. Their editor events work fine, but only when over an item, which isn't enough
...making sure everything is being added to layouts. I used QTCreator and the output is using layout.addWidget(). I am also adding the widget instance to a layout in the main window.
I was able to use the answer to register the widget as an event filter for the QTreeWidget like so:
# In __init___
# self.tree is the QTreeWidget
self.tree.viewport().installEventFilter(self)
def eventFilter(self, target, event):
"""
This widget is an event filter for the tree, so this function is triggered
automatically
"""
# Print on right-click
if (event.type() == QEvent.MouseButtonPress and
event.button() == Qt.RightButton):
print("Right Click")
# Don't block/accept the event
return False
because what you can see (and click) on QTreeWidget is actually it's viewport(). You sholud install event filter on it's viewport() instead.