I'm trying to schedule a mouse press event for a QComboBox. I was wondering if there is any way to schedule a mouse press event on the initial QComboBox click -- the click that brings up the list of items to select. I've already used the currentIndexChanged(int) signal to call a function once the user selects one of the items from the drop down menu, but I'm trying to refresh my QComboBox list with new entries once the user clicks on it. (I have a feeling that this approach may be misguided, but I guess that's another question.)
I've tried making a QComboBox subclass with def mousePressEvent(self, e), but it doesn't seem to do anything. I've also tried def mousePressEvent(self, e) in the QtGui.QWidget class that holds my QComboClass object but, unsurprisingly, that only captures mouse presses for the QtGui.QWidget.
Your current approach is misguided. Even if you could get it working, it would fail whenever the list was opened via the keyboard.
The correct way to do this is to override showPopup:
class ComboBox(QtGui.QComboBox):
def showPopup(self):
self.insertItem(0, 'Added')
super(ComboBox, self).showPopup()
Related
I'm making a user-interface in PYQT 5. It includes a QGroupBox containing several QRadioButton. When there is a toggled event I check the buttons and do some stuff. The problem is that there are two toggled events because one button is toggled on and one off so my code is always running twice.
I have been looking for an event from the QGroupBox instead. That should only happen once instead of twice when i toggle a radiobutton.
def __init__(self):
self.radioButton1.toggled.connect(self.update_stimulus)
self.radioButton2.toggled.connect(self.update_stimulus)
self.radioButton3.toggled.connect(self.update_stimulus)
def update_stimulus(self):
if self.radioButton1.isChecked():
print('1')
if self.radioButton2.isChecked():
print('2')
if self.radioButton3.isChecked():
print('3')
# Do stuff based on button positions
I tried using
self.groupBox.toggled.connect(self.update_stimulus)
But that only works if the groupbox itself is toggled. Is there any way to get a signal from the groupbox when one of the radiobuttons changes or do I have to find some way of doing it with the indivdual signals from the radiobuttons?
As #ekhumoro explains, you can add the checked parameter to the function and do your processing only if it's True:
def update_stimulus(self, checked):
if not checked:
return
if self.radioButton1.isChecked():
print('1')
if self.radioButton2.isChecked():
print('2')
if self.radioButton3.isChecked():
print('3')
Keep in mind, though, that if you want to have different radio button "groups" within the same groupbox, none of this will work properly as they will all be considered as part of the same single group: all Qt buttons (widgets that inherit QAbstractButton: QPushButton, QToolButton, QCheckBox and QRadioButton) have an autoExclusive property which is off by default except from radio buttons. This property makes all button that belong to the same parent widget automatically exclusive.
If you need different groups within the same parent, the solution is to use a QButtonGroup, which extends the exclusive functionality by limiting the membership of each button to a specific group.
def __init__(self):
# ...
self.buttonGroupA = QtWidgets.QButtonGroup()
self.buttonGroupA.addButton(self.radioButton1)
self.buttonGroupA.addButton(self.radioButton2)
self.buttonGroupA.addButton(self.radioButton3)
self.buttonGroupA.buttonToggled[QtWidgets.QAbstractButton, bool].connect(self.update_stimulusA)
self.buttonGroupB = QtWidgets.QButtonGroup()
self.buttonGroupB.addButton(self.radioButton4)
self.buttonGroupB.addButton(self.radioButton5)
self.buttonGroupB.addButton(self.radioButton6)
self.buttonGroupB.buttonToggled[QtWidgets.QAbstractButton, bool].connect(self.update_stimulusB)
def update_stimulusA(self, btn, checked):
if not checked:
return
# do something with group A
def update_stimulusB(self, btn, checked):
if not checked:
return
# do something with group B
Creation of a button group is also possible from Designer: just select at least two buttons that will be members of the same group, right click on one of them, go to the "Assign to button group" sub menu and select "New button group". To add a button to an existing group, just use the same context menu and choose the group you want to add that button to.
I'm dynamically creating creating a list of QPushButtons in a vertical layout. I'm currently using the "click" signal for a function. But I would like to use the right mouse button for an additional function. Ex. print the tooltip of that QPushButton that the mouse is over by right clicking.
but = QtGui.QPushButton()
but.setText(cur);but.setCheckable(True)
but.setStyleSheet(_fromUtf8("text-align:right;background-color: rgb(50, 50, 50);"))
but.setToolTip(longName + "." + cur)
I'm looking over at the "QMouseEvent", "setMouseTracking", "mousePressEvents". But I'm not sure how to properly use them to get my desired result.
I would also be open to a custom signal for a QPushButton on "right-click".
Usually, the right mouse click is connected to the context menu. With
but.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
but.customContextMenuRequested.connect(handle_right_click)
you can connect a handler, which could be used for the right mouse click.
If you'd like to have a pure right mouse click action, you should extend QPushButton and override mousePressEvent(self, event) and mouseReleaseEvent(self, event). Then you need to set the button's ContextMenuPolicy to QtGui.Qt.PreventContextMenu to ensure their delivery:
The widget does not feature a context menu, and in contrast to NoContextMenu, the handling is not deferred to the widget's parent. This means that all right mouse button events are guaranteed to be delivered to the widget itself through QWidget::mousePressEvent(), and QWidget::mouseReleaseEvent().
You can check the mouse buttons in your handler for the clicked signal:
but.clicked.connect(self.buttonClicked)
...
def buttonClicked(self):
if QtGui.qApp.mouseButtons() & QtCore.Qt.RightButton:
print(self.sender().toolTip())
The mouse button constants can be OR'd together, allowing you to test for different combinations of them. So the above code will detect that the right button is held down while the left button is clicked.
You can also do a similar thing with the keyboard modifiers:
if QtGui.qApp.keyboardModifiers() & QtCore.Qt.ControlModifier:
print('ctrl+click')
Hi I need to do this because, I am making a matching / memmory game, and there has to be a button (Totally separated from the ones on the current game) that when I press it, it has to show the matching cards automatically without having to touch the buttons with the mouse.
Is there a "press" function or something like that for pressing the button?
Thanks! :)
As Joel Cornett suggests in a comment, it might make more sense to simply call the callback that you passed to the button. However, as described in the docs, the Button.invoke() method will have the same effect as pressing the button (and will return the result of the callback), with the slight advantage that it will have no effect if the button is currently disabled or has no callback.
If you also want visual feedback for the button you can do something like this:
from time import sleep
# somewhere the button is defined to do something when clicked
self.button_save = tk.Button(text="Save", command = self.doSomething)
# somewhere else
self.button_save.bind("<Return>", self.invoke_button)
def invoke_button(self, event):
event.widget.config(relief = "sunken")
self.root.update_idletasks()
event.widget.invoke()
sleep(0.1)
event.widget.config(relief = "raised")
In this example when the button has focus and Enter/Return is pressed on the keyboard, the button appears to be pressed, does the same thing as when clicked (mouse/touch) and then appears unpressed again.
I have dialog:
It contains many flat QPushButtons, QTextEdit and another QPushButton. After click on 'Get list' we can see list of checked buttons in QTextEdit.
My question is how to get this functionality in some smart way. Right now I'm checking every button:
if self.ui.bq6o.isChecked():
cards.append("Q6o")
if self.ui.bk2o.isChecked():
cards.append("K2o")
if self.ui.bq3o.isChecked():
cards.append("Q3o")
if self.ui.bt7s.isChecked():
cards.append("T7s")
if self.ui.bq4o.isChecked():
cards.append("Q4o")
if self.ui.bt4s.isChecked():
cards.append("T4s")
if self.ui.b98o.isChecked():
cards.append("98o")
if self.ui.bjto.isChecked():
cards.append("JTo")
if self.ui.btt.isChecked():
cards.append("TT")
if self.ui.bq7o.isChecked():
cards.append("Q7o")
[...]
Obviously I can't like code like that. I was looking for some widget "button matrix" like, but without luck. I will be grateful for advises.
All the buttons should be children of the same widget, probably the dialog itself. Just get a handle to that widget to get all the child buttons, then loop through them and if they're checked, included their text.
parent = dialog # or whatever
cards = [widget.text() for widget in parent.children() if isinstance(widget, QPushButton) and widget.isChecked()]
You may need to include some code in the if statement to exclude the "Get List" button, or any other pushbuttons in your dialog that could be set to "checked" but shouldn't be included in cards list.
As #Brendan suggested in the other question, you could loop through them in a single list comprehension. But one other approach is to connect each buttons toggled signal to a slot that allows them to register when they are checked.
# somewhere in your class
self.checkedList = set()
def buttonChecked(self, checked):
button = self.sender()
if checked:
self.checkedList.add(button)
else:
if button in self.checkedList:
self.checkedList.remove(button)
# when you create a new button
button.toggled.connect(self.buttonChecked)
This would let you always have a set of just the checked buttons, which are self reporting. Otherwise, you would have to track them under their parent and loop to find out which are checked each time.
Update
Here is a another version that combines #Brendans loop and my signal suggestion. This might help in a situation where your buttons are a bit more spread out across your UI as opposed to be all under a single parent... but first assuming them all under a parent...
parent = dialog
for widget in parent.children():
if isinstance(widget, QPushButton):
widget.toggled.connect(self.buttonChecked)
You could repeat this in your __init__() for all the locations of your buttons and get them all registered to the slot.
I've got a QComboBox with a custom list object.
The custom list object has a custom mousePressEvent so that when the user click on one of the circles with a +/- (a twisty), the list is expanded/collapsed.
When I use the list with the combo box, when the user clicks on a twisty, the list is expanded/collapsed, but the selection is changed, and the list is hidden. How can I filter this so that when the user click on a twisty, the selection is not changed, and the list not hidden.
Additional screenshots
All of the nodes collapsed:
List hidden:
QT has a eventFilter that "captures" QEvent.MouseButtonRelease. So what I have done is installed my own eventFilter that filters QEvent.MouseButtonRelease events if the user click on a node.
In my list object I have the following method:
def mousePressEvent (self, e):
self.colapse_expand_click = False
if <user clicked node>:
colapse_expand_node()
e.accept ()
self.colapse_expand_click = True
The mousePressEvent runs before mouseReleaseEvent.
Then in the custom combobox, I filter the event:
class RevisionSelectorWidget(QtGui.QComboBox):
def __init__(self, parent = None):
QtGui.QComboBox.__init__(self, parent)
self.log_list = RevisionSelectorLogList(self)
self.setView(self.log_list)
self.log_list.installEventFilter(self)
self.log_list.viewport().installEventFilter(self)
def eventFilter(self, object, event):
if event.type() == QtCore.QEvent.MouseButtonRelease:
if self.log_list.colapse_expand_click:
return True
return False
Off the top of my head, you could subclass QComboBox and override hideEvent(QHideEvent) (inherited from QWidget)
def hideEvent(self, event):
if self.OkToHide():
event.accept()
else:
event.ignore()
Your screenshot looks like an interesting use of a combo box, I'm curious as to why you haven't used a TreeView style control instead of a list?
Edit (Mar 14 2009):
I looked at the Qt source code and it looks like when the keyboard and mouse events are captured, that as soon as qt has decided to emit the "activated(int index)" signal, "hidePopup()" has been called.
So apart from rewriting their event filter code, another option is to connect the "activated(int index)" or "highlighted(int index)" signal to a slot that can call "showPopup()" which would re-raise the list items. If you get a nasty disappear/appear paint issue you may have to get Qt to delay the paint events while the popup is visible.
Hope that helps!