QTreeWidgetItem Change - Detect Enter/ESC - c++

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.

Related

Qt autocomplete QCombobox in QTableview issue

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

Change or clear focus on QLineEdit when done editing by pressing Enter and only send one signal in Qt

I want to create a behavior in which when I finished editing a text, the focus either moves to the next child or clear the focus on the current field when I pressed "Enter". I would then get one signal to trigger a part of the code.
When I do editingFinished() and reimplement keyReleaseEvent() for "Enter" with focusNextChild() or clearFocus(), I get two signals for the text edit, one when the "Enter" is pressed, and one when the focus is change via focusNextChild() or clearFocus(). The extra signal is undesirable.
If I do returnPressed() and reimplement keyReleaseEvent() for "Enter" with focusNextChild() or clearFocus(), it would only create one signal, but I also want the signal to be created when the user exits the lineEdit via tab() or mouse click on any other line items, which would not be the case if I use returnPressed()
Ideally, I could use a signal when a lineEdit loses focus, its connected to a slot that runs the code and I can reimplement the keyReleaseEvent() for "Enter" to set the focus, but it doesn't seem like such a signal exists in Qt.
Does anyone have recommendations on how to implement this or a better way to approach it?
You can connect/disconnect signals when you want. For example, when you focus in the QLineEdit you can connect both listed signals to slot which will disconnect those two connections and move focus to the next child. Thus, you will have only one signal processed.
Another solution - you can call QLineEdit.blockSignals(true) in the beginning of the slot and call QLineEdit.blockSignals(false) in the end of the slot. In this case whatever you do in the slot you will not trigger any signal in QLineEdit and no duplicates will arise.

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.

Qt/C++: Signal for when a QListWidgetItem is checked?

In my form I have a QListWidget which contains checkable QListWidgetItems. I'm looking for a way to capture the event of a QListWidgetItem being checked/unchecked. I don't see any such signal existing for this but maybe I'm wrong. What I'm currently doing is using the QListWidget::itemClicked() signal and checking the checkState of the QListWidgetItem, but this isn't what I want because this event happens any time the item is clicked, not just went the checkmark is toggled. Can anyone give some assistance? Thanks!
Apparently no such signal is provided, your best bet is to use QListWidget::itemChanged(QListWidgetItem* item) , and scan the resulting item->checkState(). This should be a slight improvement over using itemClicked
An extra option is to use your own QAbstractListModel with a QListView. At first this does add some extra code as you need to add your own management code . But you do get a lower level access. Basically because the QListView will ask your model what to do. It will also relay input back to your listmodel where you can hook into it.
Alternatively you could subclass QStandardItemModel and catch certain edits related to changing the checkbox.

Callback for button in Qt Designer?

I just started using QtCreator tonight, and it seems it puts all of the interface stuff inside of the ui file. I followed a tutorial to create a resource for my icons, then I added them to a menu bar at the top.
I need to make a connection when one of them is clicked though, and cannot figure out how to make a callback for it.
Am I going to have to completely create them through code or is there some way to add a callback for them (rather than just making them interact with other objects).
Menu bar items are action objects. To do something when they are clicked, you need to catch the triggered() signal from the action. Read more about signals and slots here.
To do this, you need to declare a new slot in your MainWindow class. Qt also supports doing this automatically, without the need to connect anything, but I prefer doing it myself. If you're not interested, just skip this part.
First, we declare a new slot in your window class:
private slots:
void clickMenuButton();
Then, in your constructor, you need to connect the triggered signal to your new slot:
connect(ui.actionObject, SIGNAL(triggered()), this, SLOT(clickMenuButton()));
The first argument is the object that holds the signal we'll listen to (your menu button). The second is the name of the signal. The third is the object that holds the receiving slot (in this case, our window). The fourth is the slot.
And just like that, clickMenuButton() will be called whenever the action is clicked.
As I said before, Qt can also automatically connect signals to slots. The disadvantage here seems to be that you can't change the slot's name, but you don't need to connect it either.
Qt Creator supports creation of slots for widgets: in the case of your menu action, you should go to the form designer, and you should see a list of actions in your form (if you don't, find the Action Editor). Right click the action you want, and push Go to slot.... There, double click triggered().
Qt Creator will then open the new slot in your code editor, and you can do whatever you want to here!
To do that you'll need to add a QAction, add it to the menu, associate an icon with it and then create a callback for it. I'm using the VS Integration so I don't know the details of how to do it in Creator but it should be possible without creating stuff in code.
There should be somewhere an actions editor. from there you add an action, then right-click it or something to add an icon to it, then drag it do the menu and then possibly double click it to create a slot for it. This is how it works in the VS Integration atleast.