Qt autocomplete QCombobox in QTableview issue - c++

I have a QTableView which has a column with a QComboBox QItemDelegate. There is a list of completions for the combobox. When you start typing and press enter then the completion is done properly (note the capital letter)
(press enter) ->
But when I press tab it does not complete to include the capital letter.
(press tab) ->
But when I try this on a free ComboBox it does autocomplete correctly
(press tab) ->
I'm not capturing the Tab input event anywhere so I'm not sure what is causing the issue. What could it be?

Looks very much like QTableView handling the Tab key as it should -- to trigger navigation between cells, the completer is not receiving it. Of course, commitData happens, delegate works fine, but not the completer, which is not providing the editor with the proper value in the case.
Quick & easy solution could be setTabKeyNavigation(false) for the tableView.
Filtering tab key event could work as well. And finally, you could implement focusOutEvent, that would mean checking currentCompletion() in it and could be tricky a bit.
At least, that's what it looks like at first glance.

Here is how I would proceed, step by step.
Step 1: discover which widget takes the tab event.
This is a debugging technique that I find quite useful when I don't know where an event went. Set an eventfilter on the whole application, with qApp->installEventFilter(this);
Any widget can handle this, it doesn't matter.
This same widget then re-implement eventFilter(QObject* watched, QEvent *event) as:
if(event->type = QEvent::KeyPress) {
QKeyEvent *keyEvent = dynamic_cast<QKeyEvent*>(event);
if(keyEvent->key() == Qt::Key_Tab) {
qDebug() << "tab is intercepted by" << watched ;
}
}
return false ;
That should tell you which widget intercept your signal.
Step 2: stop the cullprit.
Now that we identified the cullprit (maybe the QTableView, as MasterAler suggests) , maybe you can realize you don't really want him to use this event, and maybe there is an easy way to de-activate this behavior. If so, problem solved.
Step 3: After step 2 failed, or if you don't like it
Typically because you might want the event to be proceed normally, on top of the additional functionality you define here.
Use an eventfilter (again). But this time to set in on the whole application, just on the widget that was receiving the event.
So this time, instead of qApp, we use cullprit->installEventFilter(this) ;
In the constructor of the widget that you want to use the event in. And then same as for step 1, you can detect the event and react accordingly.
Note that by returning false, the method eventFilter allows the event to follow his merry way and be handle ALSO by others.
Note: It'd probably be a bad idea to keep the eventfilter on the whole application, it'd waste the purpose of the whole event system organisation. I think it's better to keep step 1 to debugging phase only.

Try using the keyPressEvent when pressing the Tab key
if event.key() == QtCore.Qt.Key_Tab:
# autocomplete here
pass
Try reading this example here . Might be usefull. Cheers

Related

QComboBox::showPopup() steals focus from its QLineEdit

In order to create a QComboBox that can filter its values list when typing in it, I attached a slot to the QComboBox's editTextChanged-event, to open its list view popup when the user starts typing. This is done like so:
void SearchableComboBox::slotEditing(QString in_text)
{
this->showPopup();
}
Unfortunately, this immediatly steals the focus from the QLineEdit and I can't type anymore.
Calling lineEdit()->setFocus() makes no difference, and I don't want to grabKeyboard() since this creates a whole other world of pain.
Any recommendations?
This isn't really a combobox then, more of a completion listview for a lineedit. I implemented exactly that in ruqola (KDE client for rocket chat), you can see the source code at https://lxr.kde.org/source/network/ruqola/src/widgets/common/completionlistview.cpp. Notice the little dance with the focus proxy thing:
setFocusPolicy(Qt::NoFocus);
textWidget->setFocusPolicy(origPolicy);
setFocusProxy(textWidget);
and the long method slotCompletionAvailable() for positioning the completion popup at the right place...
Use the Focus Proxy method for this purpose. See https://doc.qt.io/qt-5/qwidget.html#setFocusProxy
With this the pop-up would relay its inputs to the lineedit.
In your case you could try something like
this->setFocusProxy( this->lineEdit() );
But maybe you should read how to use a QCompleter. This would provide Autocompletion while typing and is maybe also useful for you.

Is there a way to distinguish, whether a Qt widget got focus from mouse clicking or from table key pressing?

I'm using Qt5 on windows.
Is there a way to distinguish whether a Qt widget got focus from mouse clicking or from table key pressing?
Yes, there is. Override QWidget::focusInEvent and use the QFocusEvent::reason method of the focus event to get the reason!
Simple sample:
void MyWidget::focusInEvent(QFocusEvent *event) {
qDebug() << event->reason();
QWidget::focusInEvent(event);
}
Note: In case you want to get this information from an already existing widget, you can always install an event filter instead. See https://doc.qt.io/qt-5/qobject.html#installEventFilter for an example on how do do that.

QTreeWidgetItem Change - Detect Enter/ESC

I'm developing a Qt/C++ app with a QTreeWidgetItem. When a new item is created I set it setEditable and it allow me to fill directly in the UI the new name.
I'm currently using itemChanged as shown below to catch the change and save it. By default, I set the new item name to new folder and after I can change it manually. My issue is when I'm creating the item, it becomes editable and if I press Enter or Esc without any changes, the itemChanged is not generated.
Is there a command I can use based on SIGNAL/SLOT which can catch the Enter/Esc event. The goal is to triggered the same signals
connect(this, SIGNAL(itemChanged(QTreeWidgetItem*, int)),
this, SLOT(onTreeItemChanged(QTreeWidgetItem*)));
I want to connect Enter/Esc signals to onTreeItemChanged as it's done for itemChanged.
I have tried the use of itemActivated, but it's not triggered even if Enter is pressed.
connect(this, SIGNAL(itemActivated(QTreeWidgetItem*, int)),
this, SLOT(onTreeItemChanged(QTreeWidgetItem*)));
Any idea,
Seb
Sorry to write this in answer, but i still cant comment:
I had some (maybe) similar problems concerning a class derived from QCombobox, which had some spetial behaviour, when to show the popup and when not to.
Everytime Return was pressed domething happend but it wasent a QEventKeyEvent, the solution was to catch QEvent::Shortcut, because the element interpreted this key as shortcut for accept.
-- EDIT --
In such cases i often install a event fiolter and let it just write the events to the output, using a switch statement to filter out uninteresting elements, till i get the culprit.

How to know if the Gtk::ComboBoxText is popup

I am writing a simple GUI, in which I have a ComboBoxText. I write a log message when ever the user clicks on the ComboBoxText.
I have tried almost all the button release and popup signals but no results. The only thing which works is signal_changed() but I don't not need that. Please help me, below is my sample code :
myCombo->signal_button_release_event().connect(sigc::mem_fun(this,&ComboBoxText::ComboInput),false);
and here is the call back function:
bool ComboBoxText::ComboInput(GdkEventButton *pEvt) {
// Here do the desired stuffs !!
return false; }
Use GTK+ property popup-shown. Not sure about Gtkmm syntax, probably property_popup_shown().get_value().
If you need a signal to listen to, connect to popdown or notify::popup-shown (the latter is invoked when property value changes; again, I'm not sure about Gtkmm syntax).
The idea here was to fire an event when the ComboBoxText is clicked. After some readings I figured it out that the ComboBoxText does not fire any on_click event as such.
One could mask a key press event (which by the way gets fired) and call the signal handler. This might not be handy for people who specifically looking for a on_click event but for those who are working with a keyboard or touch screen device. Here is a small chunk of code :`
mCombo.add_events(Gdk::KEY_PRESS_MASK);
mCombo.signal_event().connect(sigc::mem_fun(this,&ClassName::Handler),false);
cheers :)

CancelEvent equivalent in Qt signals

I'm usually used to the .net framework and recently got the chance to work on a project using C++/Qt. In this context, I want to implement the following functionality: (to simplify things lets say I have a ListView and a Textbox/Textedit)
If I select an item of the ListView -> show the corresponding text in the textedit (done)
If I edit the text and click on another item in the list -> show a dialog that asks whether to save or cancel the changes (done). The textedit gets a signal from the list telling it that the selected item has changed.
If the user presses save -> save it back and refresh the item in the list, the new item which the user clicked gets selected in the list (done)
If the user presses cancel -> don't select the other item which the user clicked (this is where my problem is)
Basically I see two solutions (there may be more) to this:
Send a signal back from the textedit to the list, telling the list to restore the previous selection. I personally dont like this solution very much, because I have to couple them stronger (the list sends signals to the textedit and now also the other way around). Also, this 'forced' selection restoration could again trigger subsequent signals that would have to be blocked again...
A much nicer solution (I think) would be to have some Veto-mechanism in the signals. I could imagine the list to send two signals:
lets call the first one aboutToChangeSelection(proposedSelection, vetoObj)
and another one after the action is done: changedSelection(newSelection)
The first signal would be sent to the textedit, which would then eventually make use of its veto. After this, the same signal would be sent to the list itself, do the action depending of the veto and send the second signal if it actually changed the selection.
In the .NET world there exists such a mechanism, with the help of CancelEventArgs. I know that .NET events and Qt signals have quite a different working principle, but is there any Qt equivalent to archieve the same effect?
Thanks a lot for any help!
If you are using a QListWidget instead of a QListView, then you can do the following:
listen to the signal : QListWidget::itemPressed ( QListWidgetItem * item ) rather than the signal saying the item has changed (ie currentItemChanged ( QListWidgetItem * current, QListWidgetItem * previous ) in my example )
The function which opens the dialog is a slot connected to this signal. I don't think it need to be modified.
Listen to the signal : currentItemChanged ( QListWidgetItem * current, QListWidgetItem * previous ) which will be used only to remember the current and last item.
Now in your case, when the user set cancel, programmatically change back to the last item.
Now there is one problem. Which signal will be received first?? Not specified in the doc, and it can be in any order. If a user is on item A, and clicks on item B, you want lastitem to be A, and current item to be B at the moment you are handling itemPressed. So you want to handle currentItemChanged first. Fortunately you can do use Qt::DirectConnection for currentItemChanged and use Qt::QueuedConnection for itemPressed
ALTERNATIVE:
You can use an event filter on the list widget. This filter will be the one to perform the processing you described in your few steps. If the user clicks accepts, you send the event to the list view. If the user reject, you block the event. I am not sure, but it is possible that the events are not processed in a filter which will make this alternative non-viable.
Not 100% sure on this, but you could override QCoreApplication::notify( QObject * receiver, QEvent * event ) and try to find the right event and just call ignore() on it. And to tell when to ignore, you could add a new QEvent subclass, embed the target elements pointer in it and make it act as your veto event - if that is received then you know you are gonna ignore next event of some specific kind for that particular object.