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.
Related
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
i need to know what is the way to run a function when one item of the combobox is selected/clicked, i tried with the standar connect:
connect(ui->combobox,SIGNAL(clicked()),this,SLOT(clickedaction()));
... but it doesnt work.
I dont even need a custom action per element, just the same for all.
Thanks in advance.
There is no signal clicked() in QComboBox.
If you need to detect the item activation either by mouse click or enter pressed then use activated(int index).
connect(ui->combobox,SIGNAL(activated(int)),this,SLOT(clickedaction(int)));
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.
I am trying to create one event handler for button clicks and connect that to multiple buttons (creating a simple calculator where pressing each number adds its text to the lineEdit).
In C# we would use the sender object which was passed as a parameter and then cast it back to Button and get its Text or other needed property and go on.
I am new to Qt, Do we have such a thing or a similar approach in Qt? Since I couldn't get it out of the signal/slot method of Qt.
On the QObject / QWidget that receives the signal, call this->sender() (QObject::sender()), and cast it with dynamic_cast<MyWidgetType*>(...)
You can find some good examples here for linking back to the issuer of an event.
http://doc.qt.digia.com/qq/qq10-signalmapper.html
They give you different examples for
The sender() Approach (like Jamin Grey's approach below)
The Subclass Approach
The Signal Mapper Approach
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.