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.
Related
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
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()
I have a pyside2 GUI that uses QGraphicsView.
I use setDragMode(QGraphicsView.ScrollHandDrag) to make the view dragable, but i override the cursor with viewport().setCursor(Qt.ArrowCursor) on mouseReleaseEvent to avoid constantly having the open-hand in stead of the normal arrow cursor.
This is explained here: Changing the cursor in a QGraphicsView (in c++)
In the GUI there is also a QGraphicsProxyWidget with a QLabel. When the mouse is placed over the ProxyWidget, the viewport().setCursor(Qt.ArrowCursor) does not work (the moseReleaseEvent is called, so i know that setCursor is called), and when the mouse leaves the ProxyWidget, the open-hand cursor shows in stead of the arrow-cursor.
When the mouse is placed all other places in the QGraphicsView everything is working as expected.
Does anyone know why setCursor is behaving differently when the mouse is placed over a proxyWidget?
In QGraphicsView:
def mouseReleaseEvent(self, event):
QGraphicsView.mouseReleaseEvent(self, event)
self.viewport().setCursor(Qt.ArrowCursor)
def infoBoxShow(self, edge, mouse_pos):
if self.info_box is None:
self.info_box = VardeInfoBox_v2.InfoBox()
self.info_box.corresponding_edge = edge
self.info_box.setPos(mouse_pos)
self.info_box.setInfoText(edge)
self.main_scene.addItem(self.info_box)
InfoBox (As you can see i have tried to set some flags without success):
class InfoBox(QGraphicsItem):
Type = QGraphicsItem.UserType + 1
def __init__(self):
QGraphicsItem.__init__(self)
self.setFlag(QGraphicsItem.hover)
self.setZValue(4)
proxy = QGraphicsProxyWidget(self)
widget = QLabel("TEST!")
widget.setAttribute(Qt.WA_TransparentForMouseEvents)
widget.setWindowModality(Qt.NonModal)
proxy.setWidget(widget)
self.corresponding_edge = None
I'm creating an application using Qt Designer and PyQt4. I want to know how I can add an image to the QtGraphicsView widget to my desired location.
For example, when I click on the QtGraphicsView widget, I want to add an image to that exact location. I've searched online but I couldn't find anything that was of any help.
I've created a scene subclass to manage the items that will be displayed in the QtGraphicsView widget. I'm able to get the coordinates of the place where I click but I don't know how to place the item in that certain position. Below is my code:
class graphicsScene(QtGui.QGraphicsScene):
def __init__(self, parent=None):
super(graphicsScene, self).__init__(parent)
def mousePressEvent(self, event):
position = QtCore.QPointF(event.scenePos())
pixmap = QtGui.QPixmap("host.png")
pixmap_scaled = pixmap.scaled(30, 30, QtCore.Qt.KeepAspectRatio)
self.itemAt(pixmap_scaled,position.x(),position.y())
self.addPixmap(pixmap_scaled)
print "pressed here: " + str(position.x()) + ", " + str(position.y())
self.update()
def mouseReleaseEvent(self, event):
position = QtCore.QPointF(event.scenePos())
print "released here: " + str(position.x()) + ", " + str(position.y())
self.update()
class form(QtGui.QMainWindow):
def __init__(self):
super(mininetGUI, self).__init__()
self.ui = uic.loadUi('form.ui')
self.scene = graphicsScene()
self.ui.view.setScene(self.scene)
Use addItem(your_pixmap_object) to add the QPixmap to the scene. Then you can use setPos(...) on the returned QGraphicsItem (this is returned when you use addItem(...) and the insertion of the item into the scene was succcessful).The point that you pass to your setPos(...) would be the one that is part of the event (as you have done already by calling event.scenePos()).
pixmap = QPixmap(...)
sceneItem = self.addItem(pixmap)
sceneItem.setPos(event.scenePos())
If you want to use QGraphicsPixmapItem the procedure is the same as the one above but just use self.addPixmap(...) as you have done in your code.
There is one thing that you might want to handle in addition to just placing the item - the case where you press the mouse button, move the cursor somewhere else in the scene while still pressing the button and then release it. This will insert the item at the starting position of your movement event (while pressing the button and moving) however this might not be what you want to do. You have to think about whether it will be better to handle the insertion inside the mouseReleaseEvent(...) instead. It really depends on how you want things to work for this particular scenario.
Is there a way to lock docks in pyqtgraph so that the user cannot move them around?
I'm using a small touchscreen to display a pyqtgraph application with multiple docks. It is very easy for the user to accidentally move a dock. When that happens the screen becomes unusable because of the size. I would like to prevent the user from moving the docks.
However, the user must still be able to choose between docks (i.e. treat them like a tab widget).
Just to be clear, I want to prevent a dock from being detached and I want to prevent the dock from being drug to a new location.
Thanks,
Chris
I managed to disable the ability to detach and drag docks by overriding the Dock class' methods.
Dragging a dock moves it to another location. So I overrode all of the 'drag' event handlers with methods that do nothing (i.e. a no-op).
Double-clicking on a dock's label will cause the dock to detach. So, I overrode the dock's label's double-click event handler with a no-op.
Replace Dock with MyDock in your code. UPDATE: I added code to override the drag methods for the DockArea too because I was still able to move DockAreas around.
Here is the code:
##
# This class is used to eliminate a standard Dock class' ability to detach and
# move (i.e. dragging this Dock will have no effect)
#
class MyDock(Dock):
def __init__(self, name, area=None, size=(10, 10), widget=None, hideTitle=False, autoOrientation=True):
# Initialize the baseclass
#
Dock.__init__(self, name, area, size, widget, hideTitle, autoOrientation)
# Override the label's double click event. Normally double clicking
# the dock's label will cause it to detach into it's own window.
#
self.label.mouseDoubleClickEvent=self.noopEvent
def dragEventEnter(self, ev):
pass
def dragMoveEvent(self, ev):
pass
def dragLeaveEvent(self, ev):
pass
def dragDropEvent(self, ev):
pass
def noopEvent(self,ev):
pass
class MyDockArea(DockArea):
def dragEventEnter(self, ev):
pass
def dragMoveEvent(self, ev):
pass
def dragLeaveEvent(self, ev):
pass
def dragDropEvent(self, ev):
pas