with help of visitors of this forum I have made a custom context menu in QListWidget. Now Im trying to solve a problem that the menu is showing up when the user have rightclicked on the widget but the mouse wasnt over any item. When this situation occurs I want the menu to not show up.. Ive been thinking about obvious condition - is the mouse over some item in the widget? But I dont know how to do this.
thank you in advance for all the answers :)
Since you do not show how you are currently creating your context menu, I will assume from the beginning...
Set your widget's contextMenuPolicy to Qt::CustomContextMenu
Connect the widgets customContextMenuRequested signal to a slot that will show your custom menu
The slot will receive a QPoint in local widget coordinates. You can use this QPoint to find out which item is under that point with QListWidget.itemAt(QPoint). You can then validate that pointer. If it's a valid item, show your menu at that point (or some offset of it that suits you). A valid item means a non-null pointer.
Here is an example of what it looks like in PyQt. You can translate this to Qt
class Widget(QtGui.QWidget):
def __init__(self,parent=None):
super(Widget, self).__init__(parent)
self.list = QtGui.QListWidget()
self.list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.list.customContextMenuRequested.connect(self.handleContext)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.list)
self.list.addItems(["one", "two"])
def handleContext(self, pos):
item = self.list.itemAt(pos)
if item is not None:
menu = QtGui.QMenu("Context Menu", self)
menu.addAction("FOO")
ret = menu.exec_(self.list.mapToGlobal(pos))
In PyQt4, if the item at the given position is None, then it was an empty space. In Qt, it should be a null pointer.
Related
I am creating a node graph and I want to be able to click the empty space in a scene and middle-mouse-drag to navigate without deselecting the currently selected items in the scene. Any suggestions?
I can block the middle click in the view's mousePressEvent and get the right behavior but then I no longer have middle-mouse click events working on items in the scene. I don't mind a middle-click resulting in a single selection when clicking on an item in the scene, but if I middle-click the empty space in the scene I don't want the selection altered.
This didn't cover the more complex behavior I am looking for: PyQt. How to block clear selection on mouse right click?
I didn't try using an eventFilter as I assume the issue would be the same
I am using PyQt/PySide, FWIW.
Before I roll my own workaround I thought I'd post here for the correct way or at least other workaround ideas.
Some workaround ideas:
Block the mousePressEvent to the scene but iterate over child items to deliver it directly
Restore selection while still in the mousePressEvent in the scene. Probably bad for performance at scale but simple I suppose.
Any feedback would be great!
[Edit:]
Here is my python version of the answer. Code tested. In my QGraphicsScene derived class:
def mousePressEvent(self, event):
# Prevent the QGraphicsScene default behavior to deselect-all when clicking on
# empty space by blocking the event in this circumstance.
item_under_the_mouse = self.itemAt(event.scenePos())
if event.button() == QtCore.Qt.MidButton and not item_under_the_mouse:
event.accept()
else:
super(GraphScene, self).mousePressEvent(event)
In your QGraphicsScene::mousePressEvent derived implementation, if it's a middle mouse click, check for items under the mouse click. If there aren't any, then accept the event and don't call the base class implementation. If something is under the click, then just call the base implementation; you don't have to try to reimplement that yourself. I think this is the general idea:
void MyScene::mousePressEvent (QGraphicsSceneMouseEvent *evt)
{
if ((evt->buttons () & Qt::MidButton) && items (evt->scenePos ().count ())
{
QGraphicsScene::mousePressEvent (evt);
}
else
{
evt->accept ();
}
}
I'm not sure if the accept is required in this case or not. I haven't compiled or tested this, but hopefully, it's helpful to get you going in the right direction.
I have a program that creates a grid of buttons using PyQt (all QPushButtons in a QGridLayout), like this:
I was wondering how I can drag my mouse while holding mouse1 down to run each button's function for each one that the mouse is dragged over. Right now, when dragging and holding, only the button the click was started on is triggered, and no other buttons can be selected until mouse release. Is there a function that just runs it on press or release, or unselects the button after the function is ran?
For reference, my code for the currently selected button is as follows:
self.button.pressed.connect(lambda: self.click_func(self_global, x, y,
btn_id))
The function isn't important, and would just take up more room here, but I hope you get the point.
You could use an eventFilter. This allows you to send events to another QObject (usually the parent widget of the buttons) to decide what to do with them.
class MyWidget(QtGui.QWidget):
def createButton(self):
button = QtGui.QPushButton()
button.setMouseTracking(True)
button.installEventFilter(self)
return button
def eventFilter(self, obj, event):
if isinstance(obj, QtGui.QPushButton):
if event.type() == QtCore.QEvent.Enter:
if event.buttons() & QtCore.Qt.LeftButton:
print 'Mouse Pressed Over Button'
return False
Looking at your example, you may want to also consider using a QGraphicsView and QGraphicsScene. They are good building blocks for creating truly custom widgets. It's possible you may be able to hack QPushButtons to behave in a way they weren't intended, but for custom drawn widgets, QGraphicsViews are the way to go.
I have some rectangle link area on my widget. What is the best way to make cursor Qt::PointingHandCursor when it is in this area?
The QWidget class has a cursor property that you can set with the cursor you wish displayed when the mouse is above it.
EDIT:
Without more detail on what you are trying to achieve, I can only assume you're making your life much more difficult than it needs to be. You can create a QLabel widget to handle the link and then place the label on the menubar automatically.
QLabel *link = new QLabel("<a href='http://doc.qt.io'>Qt Documentation</a>");
menuBar()->setCornerWidget(link);
All the text formatting, cursor display and user interactions are handled by existing code in the Qt classes. The only thing you need to do yourself is to handle what happens when the user clicks on the link, that you can do by connecting a slot to the QLabel::linkActivated(const QString &) signal.
I am programming in C++ in QT and trying to make a UI with dynamic tabs having tables inside each of them. For doing the same, I had my TabWidget in the main window, and another widget with just the tableView. As the tabs are dynamically being added to the main window by a button click, I make a new object of my widget and put it in that.
I also have another version of the application in which there are no tabs, just a tableView in the main window.
I am unable to open the context menu in the former case, while it works perfectly for the latter.
I am using the signal "customContextMenuRequested" in both the cases. Don't understand what I need to add for it to work when the tableView is in a child widget.
Some help please?
Thanks already!
Did you check that nothing is involving QAbstractScrollArea, it's possible that in this case it would signal/slot as expected.
This signal is emitted when the widget's contextMenuPolicy is Qt::CustomContextMenu, and the user has requested a context menu on the widget. The position pos is the position of the context menu event that the widget receives. Normally this is in widget coordinates. The exception to this rule is QAbstractScrollArea and its subclasses that map the context menu event to coordinates of the viewport() .
for my application I'm trying to put login control into a QMenu and am struggling with controlling the focus policy.
This is my custom login widget:
class LoginWidget(QWidget):
def __init__(self, parent=None):
super(LoginWidget, self).__init__(parent)
mainLayout = QVBoxLayout()
layoutH = QHBoxLayout()
nameField = QLineEdit()
pwdField = QLineEdit()
pwdField.setEchoMode(QLineEdit.EchoMode(2))
btnSubmit = QPushButton('log in')
btnSubmit.setIcon(IconCache.getIcon('login'))
for w in (nameField, pwdField):
layoutH.addWidget(w)
mainLayout.addLayout(layoutH)
mainLayout.addWidget(btnSubmit)
self.setLayout(mainLayout)
I then add the above widget to my menu like this:
app = QApplication([])
menu = QMenu()
settingsAction = QAction('settings', menu)
loginAction = QWidgetAction(menu)
loginAction.setDefaultWidget(LoginWidget())
menu.addAction(settingsAction)
menu.addAction(loginAction)
btn = QToolButton()
btn.setText('menu button')
btn.setMenu(menu)
btn.setPopupMode(QToolButton.InstantPopup)
btn.show()
sys.exit(app.exec_())
The problem is that when you open the menu, click into the name field to fill in the user name, then hit the tab key, focus jumps to the "settings" action rather than to the password widget inside the LoginWidget.
I tried setFocusPolicy(Qt.StrongFocus) on the LoginWidget as well as it's pwdField but to no avail.
Can this be done?
Thanks in advance,
frank
The QMenu has special handling for Tab / Backtab, which effectively converts them into Up / Down arrow key presses.
However, the real source of the problematic behaviour is the focusNextPrevChild method, which keeps forcing the focus back to the menu. Fortunately, this method is virtual, so it can be overridden in a subclass, like so:
class Menu(QtGui.QMenu):
def focusNextPrevChild(self, next):
return QtGui.QWidget.focusNextPrevChild(self, next)
This will restore normal tabbing between child widgets.
To also enable keyboard navigation from child widgets back to normal menu items, make sure the LoginWidget has a focus proxy, like so:
class LoginWidget(QtGui.QWidget):
def __init__(self, parent=None):
...
self.setFocusPolicy(QtCore.Qt.TabFocus)
self.setFocusProxy(nameField)
http://qt-project.org/doc/qt-4.8/qwidget.html#setTabOrder
http://qt-project.org/doc/qt-4.8/focus.html#tab-or-shift-tab
Tab or Shift+Tab
Pressing Tab is by far the most common way to move focus using the keyboard. (Sometimes in data-entry applications Enter does the same as Tab; this can easily be achieved in Qt by implementing an event filter.)
Pressing Tab, in all window systems in common use today, moves the keyboard focus to the next widget in a circular per-window list. Tab moves focus along the circular list in one direction, Shift+Tab in the other. The order in which Tab presses move from widget to widget is called the tab order.
You can customize the tab order using QWidget::setTabOrder(). (If you don't, Tab generally moves focus in the order of widget construction.) Qt Designer provides a means of visually changing the tab order.
Since pressing Tab is so common, most widgets that can have focus should support tab focus. The major exception is widgets that are rarely used, and where there is some keyboard accelerator or error handler that moves the focus.
For example, in a data entry dialog, there might be a field that is only necessary in one per cent of all cases. In such a dialog, Tab could skip this field, and the dialog could use one of these mechanisms:
If the program can determine whether the field is needed, it can move focus there when the user finishes entry and presses OK, or when the user presses Enter after finishing the other fields. Alternately, include the field in the tab order but disable it. Enable it if it becomes appropriate in view of what the user has set in the other fields.
The label for the field can include a keyboard shortcut that moves focus to this field.
Another exception to Tab support is text-entry widgets that must support the insertion of tabs; almost all text editors fall into this class. Qt treats Ctrl+Tab as Tab and Ctrl+Shift+Tab as Shift+Tab, and such widgets can reimplement QWidget::event() and handle Tab before calling QWidget::event() to get normal processing of all other keys. However, since some systems use Ctrl+Tab for other purposes, and many users aren't aware of Ctrl+Tab anyway, this isn't a complete solution.
So you probably will want to use:
QWidget.setTabOrder( nameField, pwdField )
QWidget.setTabOrder( pwdField, btnSubmit )
or something similar.
Hope that helps.