I'm trying to implement the following functionality based on QPlainTextEdit - by default it should show a "please enter something here" message, on click, it would vanish and allow editing.
To do so I need to intercept whether the widget has been clicked. Can I do so without sub classing QPlainTextEdit?
Can the parent's widget onMousePressEvent obtain which child's widget the event belongs to?
Can I do so without sub classing QPlainTextEdit?
Yes, you can implement an event filter:
http://qt-project.org/doc/qt-4.8/eventsandfilters.html#event-filters
Essentially, you can filter out the mouse events destined for the plain text edit. Whenever your filter encounters a mouse press event, you can clear the contents of the plain text edit.
Can the parent's widget onMousePressEvent obtain which child's widget
the event belongs to?
Not without event filtering. Qt's event propagation system sends events to child widgets first and then only to parent widgets if the child widget does not accept the event.
Related
What is the difference between hide,close and show of pushbutton or any widget in terms of memory?
Which is better if I don't want to use widget again?
First as said #Hayt, read the documentation.
For the actual answer:
hide() is the same as setVisible(false).
show() is the same as setVisible(true).
close() attempts to close the widget by triggering a QCloseEvent, if the event is accepted the result is:
The same as calling hide() if Qt::WA_DeleteOnClose attribute is not set on the widget which is the default.
The same as calling deleteLater() if Qt::WA_DeleteOnClose is set.
In term of memory, any of the 3 will not change anything (except for close() if you have set Qt::WA_DeleteOnClose). If you do not want to use the widget ever, the best is to delete it:
delete pointerToMyWidget;
or
pointerToMyWidget->deleteLater();
The second form is generally safer as the 1st one can be dangerous depending on where your write it. (e.g you delete it in a slot called by a signal emitted by the widget you delete).
According to Qt, you can read this :
CLOSE :
Closes this widget. Returns true if the widget was closed; otherwise
returns false.
First it sends the widget a QCloseEvent. The widget is hidden if it
accepts the close event. If it ignores the event, nothing happens. The
default implementation of QWidget::closeEvent() accepts the close
event.
If the widget has the Qt::WA_DeleteOnClose flag, the widget is also
deleted. A close events is delivered to the widget no matter if the
widget is visible or not.
The QApplication::lastWindowClosed() signal is emitted when the last
visible primary window (i.e. window with no parent) with the
Qt::WA_QuitOnClose attribute set is closed. By default this attribute
is set for all widgets except transient windows such as splash
screens, tool windows, and popup menus.
.
HIDE : Hides the widget. This function is equivalent to
setVisible(false).
Note: If you are working with QDialog or its subclasses and you invoke
the show() function after this function, the dialog will be displayed
in its original position.
.
SHOW : Shows the widget and its child widgets. This function is
equivalent to setVisible(true).
If you don't need to use your widget, call close(). You can manage the event to destroy your widget.
hide() only hides. It's only graphical, you can't see your widget but you don't destroy it.
But I think that the name fo the function are enough explicit to understand!
I have my custom widget inherited from QWidget, and I've connected the widget's menu-calling signal to my slot.
connect(m_ontologyView, SIGNAL(customContextMenuRequested(QPoint)), SLOT(showContextMenuSlot(QPoint)));
Now I want user to be able to change button calling the context menu. Normally it's called with right mouse button, but how do I tell the widget to call the menu with a button of my choice?
I'm on Qt 5.4.0
Instead of using QWidget::customContextMenuRequested, you will need to reimplement the widgets mouse event functions, QWidget::mousePressEvent, QWidget::mouseReleaseEvent and QWidget::mouseMoveEvent. Inside of these events, you can then show you menu using QMenu::popup. (The point can be extracted from the mouse events).
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.
In my application, I have one tableview of items, and a side-panel "preview":ing the latest selected item.
I want clicking on an item to change the selection, and double-clicking to cause a "run"-action to be performed. More specifically, I want the "run"-action (including key-navigation and pressing enter) to be bound to the "activation" of the item in the table-row.
My problem is; single-clicks does not only change the selection, but fires the "activated" signal on the item. I would like to tweak it such that:
Navigation Keys, Single Mouse Click: Selection-change, update preview-panel
Enter Key, Double Mouse Click: Activate/run/open action triggered.
Is there a nice clean way to do it, or are overriding the onclick/doubleclick events my best option? Or is there some other tabular list-widget better suiting my needs?
I would connect the slot for the preview action to the currentChanged() signal of the table view's selectionModel(). This covers single clicks and key navigation.
Then there's two options for the double clicks and Enter key presses:
Subclass your tableview, override doubleClickEvent() and keyPressEvent() and fire your custom signal in there, with maybe the model index or something else as an argument. Then just connect your run method to your own signal as you have full control over when it is fired.
If you don't want to subclass, you can use the installEventFilter() mechanism.
Either I'm getting your approach wrong or I'm too tired, but if you want to trigger a run event you should avoid the activated signal completely. Set the signal slot mechanism so that your double click and Enter key press event trigger your run() function, and then the single click/nav buttons should trigger the 'activated' slot which will return your index in the tableview.
I'm pretty certain Qt wants you to be explicit about which signal points to which slot or it'll ignore it or point to a default.
I have placed a few buttons in a Qgraphicsscene, but I don’t know how to navigate to the button from a keyboard.
How would I set the focus to a button from the keyboard?
I assume that you used QGraphicsScene::addWidget() to add the button to the scene? It gives you a proxy object back, QGraphicsProxyWidget *, which inherits QGraphicsItem::setFocus(). But remember that it needs to have set the ItemIsFocusable flag and needs to be visible and active as well.
Additionally (from the setFocus() documentation):
As a result of calling this function, this item will receive a focus in event with focusReason. If another item already has focus, that item will first receive a focus out event indicating that it has lost input focus.