Exclusive checkbox in QListView - c++

I'm trying to do exclusive checkboxes as a QListView items. I'm using QStandardItemModel as a model with QStandardItem's.
I'm adding items to the list dynamically and set it checkable:
QStandardItem *item = new QStandardItem(treeView->model()->data(index).toString());
item->setCheckable(true);
m_categoriesModel->appendRow(item);
I tried connect all items to QSignalMapper but QStandardItem doesn't have checked(bool) signal (basically it does not have any).
Is there any way to solve the problem?

You can always make it in the way described below. Firstly connect the clicked signal of ListView to the slot which will handle your items click. Secondly inside of the slot you can get the item from QModelIndex and check the state of the item. Below is pseudo code:
For example, in constructor of ListView:
connect(this, SIGNAL(clicked(QModelIndex)), this, SLOT(_handleItemClicked(QModelIndex)));
Slot of ListView:
void ListView::_handleItemClicked(QModelIndex index)
{
QStandardItem* item = _model->itemFromIndex(index);
if( item->checkState() == Qt::Checked) qDebug() << "Checked!";
}

There actually is a class for exactly doing this: QButtonGroup
It's easy to use:
QButtonGroup *group = new QButtonGroup(this);
group->setExclusive(true);//now only one will be checked at a time
//add all buttons
group->addButton(this->ui->myFirstCheckbox);
//...
... at least for manually added buttons. Of course you can use it for the model too, but it would require you to find all the checkbox elements inside the view...

Related

QTreeView: how to abort selection change

I have a QTreeView in my widget. When an item is selected in the view, I have
a signal handler that updates a series of information widgets in a detail window
about the selected item. The user can then edit the item details and commit the
changes back to the model.
If the data in the details view has been edited when this selection change
happens, I present a confirmation dialog to the user before replacing the data
when a new item is selected. If the user cancels, I want to set the selection of
the tree back to what it was before.
I have my slot connected like so:
auto selection_model = treeview->selectionModel();
connect(
selection_model, &QItemSelectionModel::currentChanged,
this, &Editor::on_tree_selection_changed
)
Inside my slot, the code is structured as follows:
void on_tree_selection_changed(QModelIndex const& index, QModelIndex const& previous)
{
if(not confirm_editor_discard())
{
// user does not want to override current edits
log.trace("cancel item selection change");
using SF = QItemSelectionModel::SelectionFlags;
auto sm = treeview->selectionModel();
sm->setCurrentIndex(previous, SF::SelectCurrent | SF::Rows);
}
else
{
// user wants to discard, so update the details view.
log.trace("discard pending edits");
set_details_from_model(index);
}
}
However, the setting of the current index back to the previous does not seem to
affect the TreeView; it still displays the newly selected item as selected, and
the interface becomes non-coherent since the item displayed in the details is
not the one shown as selected in the tree.
The intended behaviour is to re-select the previously selected item, as if no
new selection was made at all.
Apparently the QTreeView ignores any updates from the selection model while the currentChanged slot is being called.
The solution here was to call the slot as a QueuedConnection, so the connect line would look like this:
connect(
selection_model, &QItemSelectionModel::currentChanged,
this, &Editor::on_tree_selection_changed,
Qt::QueuedConnection // <-- connection must be queued.
)
This will ensure that the change in selection of the selection model will not happen directly inside a slot call.

Which UI item allows dynamic expansion on the go in Qt?

Suppose I have a single text box and a radio button placed below the text box .
What I want is that if the user enters some text and ALSO clicks the radio button a similar text box and a radio button should appear just below. And similar thing should happen if the user does that for new UI items.
Any suggestions what classes I should be looking for?
The relevant class you need to handle the inserting of new widgets is one of the QLayout subclasses, most likely QVBoxLayout it sounds like. This will allow you to insert a new textbox and radio button at the bottom of the layout, which will then automatically expand to fit these new widgets.
Inserting the new objects is pretty straightforward. Connect a slot to a signal of the QRadioButton that's at the bottom of the layout, like the QRadioButton::toggled or QRadioButton::clicked signals. This slot will check if the above textbox has any text in it, and, if so, insert a new textbox/radio button pair below them.
To be clear, this slot should be a method of the widget that uses the layout to arrange its sub-widgets. For example, if you're putting all these objects in a QGroupBox, then that's the object to which you'd add the slot I'm describing.
Here is an (untested) example:
class Group : public QGroupBox {
Q_OBJECT
public:
Group(QWidget* parent = nullptr) : QGroupBox(parent)
{
layout = new QVBoxLayout(this);
insertNewRow();
}
private slots:
void insertNewRow(bool checked = true) {
/* You might want to make this remove the row if the button is unchecked */
if (!checked)
return;
/* Only add row if text box is non-empty */
if (textBoxes.isEmpty() || textBoxes.last()->text().isEmpty())
return;
/* Disconnect slot for previous radio button */
QObject::disconnect(radioButtons.last(), 0, 0, 0);
/* Add new text box and button, labeled Button 1, Button 2, etc. */
textBoxes.append(new QLineEdit("", this));
radioButtons.append(new QRadioButton(QString("Button %1").arg(textBoxes.size())), this));
/* Connect signal/slot to add new row when clicked */
QObject::connect(radioButtons.last(), &QRadioButton::toggled, this, &Group::insertNewRow);
}
private:
QVBoxLayout* layout;
QList<QLineEdit*> textBoxes;
QList<QRadioButton*> radioButtons;
};

QCheckbox: more states than checked and unchecked

Is it possible to have more states for QCheckbox than Qt::Checked and Qt::Unchecked?
I have a QTreeWidget and if an Item is checked I want the parent to show a filled checkbox (some state like "Child checked") and the children should then have a state like "parent checked". If latter would be too complex to achieve I think the normal Qt::Checked would also work fine. But how to achieve the first? Here is my code how I am currently adding items with checkboxes:
QTreeWidgetItem* Options::folderMonitoringCreateTreeCheckbox(QDir *dir, bool state, QTreeWidget *parent)
{
QString text = dir->absolutePath().section('/', -1, -1, QString::SectionSkipEmpty);
QTreeWidgetItem *newItem = new QTreeWidgetItem(parent);
newItem->setText(0,text);
newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable);
newItem->setCheckState(0, Qt::Unchecked);
newItem->setToolTip(0, dir->absolutePath());
return newItem;
}
Here is a Screenshot for what I want to achieve (screenshot taken from MediaMonkey):
Thank you!
I think you are looking for Qt::PartiallyChecked, the description of it says:
The item is partially checked. Items in hierarchical models may be partially checked if some, but not all, of their children are checked.

How to send a signal from delegate

I have an user interface that uses a TableView. It has 3 columns. The last column has a comboBox. All the data is inserted with the delegate. The problem is I can not find a method to send a signal to a public slot of the user interface class when the combobox index is changed.
With the delegate I already know the current index. Do someone know a method to send this index to the ui? I do not think the only possible solution is with signals and slots. Is a direct solution to extract this data?
EDIT
What I understand it is I have to do something like this:
void Delegate :: setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
if(index.column() == COL_Coordonate) // test if we are at the last column
{
QComboBox *comboBox = static_cast<QComboBox*>(editor);
model -> setData(index, comboBox -> currentIndex(), Qt::EditRole);
emit dataChanged(comboBox -> currentIndex(),comboBox -> currentIndex()); // something like this you have in mind?
}
}
And how can I receive that index in the user interface? I create my model in there something like:
QStandardItemModel *model;
Delegate *mydelegate;
And use them like:
mydelegate = new Delegate(this);
model = new QStandardItemModel(0, 3, this); // I add rows dynamically
ui -> tableView -> setModel(model);
ui -> tableView -> setItemDelegate (mydelegate);
I add data with the delegate when I press a button. Do I need do trigger a slot from this interface? If so someone can please provide a sample of code about how do I do this?
You have a QComboBox instance. You can connect to its signals. What do you not know? In any case, you should not be connecting to the delegate: it is an implementation detail of the view. You should interface with the model, not with the view. Connect to the model's dataChanged signal!

QSortFilterProxyModel how to handle QStandardItems correctly

I have QTreeView with some items and search QLineEdit with connected slot on textEdited signal.
With this code:
QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(messagesModel);
proxyModel->setFilterFixedString(text);
ui.treeView->setModel(proxyModel);
text filtering is ok, but when I clicked on QTreeView QStandardItems checkboxes (after proxy model assigned to QTreeView), I have the program crashes in slot, that connected to this QTreeView original model (before proxy was assigned).
What is the right way to processing item checkbox clicks? Need I use new connect/slot to processing model changes, or I can use the same code for original model with some changes? I just need to hide filtered items in QTreeView. In QTreeWidget is hide() method, does QTreeView has something like this, or QSortFilterProxyModel - is what I need? Thx!
UPD crashed in slot, connected to treeView:
auto item = messagesModel->itemFromIndex(index); // item is NULL because proxyModel is set for TreeView now
if(item->whatsThis().isEmpty()) return; // error below
#ifndef QT_NO_WHATSTHIS
inline QString whatsThis() const {
return qvariant_cast<QString>(data(Qt::WhatsThisRole));
}
inline void setWhatsThis(const QString &whatsThis);
#endif
because I set proxyModel to treeView, but messagesModel have whatsThis...
I changed my code with that:
QStandardItem* item;
if(ui.leFilter->text().isEmpty())
item = messagesModel->itemFromIndex(index);
else
item = messagesModel->itemFromIndex(proxyModel->mapToSource(index));
if(item->whatsThis().isEmpty()) return;
and it works. Is that correct way? Proxy model is member of my UI class ... not local.
UPD how can I update source model when checkbox checked in proxyModel?
UPD 2 I have load "original" model for QtreeView and show it. When I edit text in QListEdit, I use proxyModel (code from 1st post). When text edited, I have check checkboxes in QtreeView (now proxyModel is active) and at this step all is ok. But when I do some changes in UI, in QTreeView set the original model and it has no changes that was made for proxyModel. How can I notify and update items in source Model with new data from proxyModel?
UPD3 Yes, source model is also modified ... I have just clear it)