QPushButton has a signal which is named clicked(), and we can catch click events through it. Is there a method or signal which catches hover and leave events?
How can I catch mouse-over button and mouse-leave button, like this:
button = QPushButton(window)
button.clicked.connect(afunction)
Note: I use python3.
You need to subclass the QPushButton class and reimplement the enterEvent and leaveEvent:
class Button(QPushButton):
def __init__(self, parent=None):
super(Button, self).__init__(parent)
# other initializations...
def enterEvent(self, QEvent):
# here the code for mouse hover
pass
def leaveEvent(self, QEvent):
# here the code for mouse leave
pass
You can then handle the event locally, or emit a signal (if other widgets needs to react on this event you could use a signal to notify the event to other widgets).
Related
I have the next problem:
A created a custom widget (simple derived QWidget class). When I middle-mouse click on it - it creates another QWidget class (sort of context menu). When I release middle-mouse button - that context widget disappears. So that works. What does not work is - that context widget also has some content added, like other small widgets, icons, etc and they all have their own custom events (simple example - enterEvent and leaveEvent with prints indicating those events). But those inner widget events are not working, they are blocked while I keep middle-mouse pressed. When I release it - context widget disappears. Would like to know if there is any solution to let inner widgets'events work as expected.
Here is a minimal example where inner widget does not run mouse events as they are blocked by MainWidget event:
from PyQt5 import QtWidgets, QtGui, QtCore
class SomeSmallWidget(QtWidgets.QWidget):
def __init__(self, increment=1, globalValue=0):
super(SomeSmallWidget, self).__init__()
# UI
self.setMinimumWidth(40)
self.setMaximumWidth(40)
self.setMinimumHeight(40)
self.setMaximumHeight(40)
self.mainLayout = QtWidgets.QVBoxLayout()
self.setLayout(self.mainLayout)
def enterEvent(self, event):
print('Entered') # NOT WORKING
super(SomeSmallWidget, self).enterEvent(event)
def leaveEvent(self, event):
print('Leaved') # NOT WORKING
super(SomeSmallWidget, self).leaveEvent(event)
class ContextWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(ContextWidget, self).__init__()
self.setMouseTracking(True)
# position
point = parent.rect().topLeft()
global_point = parent.mapToGlobal(point)
self.move(global_point - QtCore.QPoint(0, 0))
self.innerWidget = SomeSmallWidget() # Here is that small widget, which event does not work
self.mainLayout = QtWidgets.QVBoxLayout()
self.setLayout(self.mainLayout)
self.mainLayout.addWidget(self.innerWidget)
class MainWidget(QtWidgets.QLineEdit):
def __init__(self, value='0'):
super(MainWidget, self).__init__()
self.setMouseTracking(True)
self.popMenu = None
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.MiddleButton: # while we keep MMB pressed - we see context widget
self.popMenu = ContextWidget(parent=self)
self.popMenu.show()
super(MainWidget, self).mousePressEvent(event)
def mouseReleaseEvent(self, event):
if event.button() == QtCore.Qt.MiddleButton:
if self.popMenu:
self.popMenu = None
Events are not blocked, all mouse events are sent to widget that was under cursor when mouse button was pressed. Usually (always) it makes more sense. Imagine two buttons next to each other. Suppose user pressed one, moved cursor and released over second button. What was his intention? He probably changed his mind. If button triggers action on mouse press - this options will not be available and it's probably too soon, if button triggers action on mouse release, which button should recieve mouserelease event? If we send mouseevent to second button - that was not pressed it will trigger action that used didn't want. If we dont send mouserelease event to first button - it will stay in sunken mode. Imagine user is selecting text in lineedit, and while selecting he leaves lineedit and goes to other widgets, should they react somehow, and should focus be switched? Probably not. So there is only one active window and only one focused widget at a time and it receives keyboard and mouse input and reacts to it. Most of menus are shown after mouserelease and closes on next mouseclick, providing better user experience.
However, If you still want your widget to receive mouse events, you can achive this by translating it from ContextWidget to SomeSmallWidget like this:
class SomeSmallWidget(QtWidgets.QWidget):
...
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.fillRect(self.rect(), QtCore.Qt.blue)
def onMouseEnter(self):
print('onMouseEnter')
def onMouseLeave(self):
print('onMouseLeave')
class MainWidget(QtWidgets.QLineEdit):
...
def mouseMoveEvent(self, event):
if self.popMenu:
self.popMenu.mouseTest(event.globalPos())
class ContextWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
...
self._inside = False
def mouseTest(self, p):
widget = self.innerWidget
rect = QtCore.QRect(widget.mapToGlobal(QtCore.QPoint(0,0)), widget.size())
inside = rect.contains(p)
if inside != self._inside:
if inside:
widget.onMouseEnter()
else:
widget.onMouseLeave()
self._inside = inside
Notice I added paintEvent to see the widget bounds.
I have class MainWindow and it have qscintilla editor, i want to add listener to editor mousePressEvent
class MainWindow(QtWidgets.QMainWindow, gui.Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.editor.mousePressEvent = self.on_editor_click
def on_editor_click(self, QMouseEvent):
// here i want add my code
return QsciScintilla.mousePressEvent(self, QMouseEvent)
If i override mousePressEvent - editor will broke (mouse clicks will not work). I tried call initial mousePressEvent, but it dont work, app crashing
Assigning the mousePressEvent method to another function is not correct, mousePressEvent is not a signal, it is a function that is part of QsciScintilla.
A possible solution is that you create a personalized QsciScintilla that emits a signal created as shown below:
class ClickQsciScintilla(QsciScintilla):
clicked = QtCore.pyqtSignal()
def mousePressEvent(self, event):
self.clicked.emit()
QsciScintilla.mousePressEvent(self, event)
Then you create an instance of ClickQsciScintilla and connect to that signal:
self.__editor = ClickQsciScintilla()
self.__editor.clicked.connect(self.on_editor_click)
Your handler:
def on_editor_click(self):
print "Editor was 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.
Is there any list of the signals that can be use with PyQT4 or at least there is one that is the opposite of lostFocus()?
There is a QFocusEvent event generated by 'QWidget', but not a signal. There is however a convenient event handler that catches these events: focusInEvent.
You can add your own signal by reimplementing this handler. For example (not tested):
class MyWidget(QtGui.QWidget):
focus_in = QtCore.pyqtSignal(int, name='focusIn')
def focusInEvent(self, event):
self.focus_in.emit()
QtGui.QWidget.focusInEvent(self, event)
Now you get a focusIn signal.
I want to put my custom widget in a QScrollArea, but in my custom widget, I reimplemented wheelEvent(e) and it never gets called.
I'm fine with the scroll area not having its mouse wheel scrolling functionality. I just need those wheelEvents to call my handler. I tried handling the events out at the level of the main window but I only got them when the scroll widget was at one of its extremes and couldn't have moved any further anyways, I need all of them.
Heres a simplified version of my code:
class custom(QWidget):
def __init__(self, parent=None):
super(custom, self).__init__(parent)
self.parent = parent
def wheelEvent(self,event):
print "Custom Widget's wheelEvent Handler"
class mainw(QMainWindow):
def __init__(self, parent=None):
super(mainw, self).__init__(parent)
scroll = QScrollArea()
self.tw = thread_widget(scroll)
scroll.setWidget(self.tw)
self.setCentralWidget(scroll)
def wheelEvent(self,event):
print "Main Window's wheelEvent Handler"
Can someone explain to me how it is determined which event handler gets the events in this situation?
I figured out that its got something to do with the installEventFilter method of QObject, but I couldn't get the example to work so I said to hell with this and changed my plan completely.
problem solved
You can install a eventFilter in your custom class
class custom(QWidget):
def __init__(self, parent=None):
super(custom, self).__init__(parent)
self.parent = parent
self.installEventFilter(self)
def eventFilter(self, qobject, qevent):
qtype = qevent.type()
if qtype == QEvent.Wheel:
... wheel event logic
return True
# parents event handler for all other events
return super(custom,self).eventFilter(qobject, qevent)