QTableview how to save checkbox value c++ - c++

I am referring to the code in this answer and this to create a tableview with checkboxes.
I am trying to create a table, that shows a list of student in my database with checkbox alongside.
Now that i have done that,
Question is:
How do I save the value of the checkboxes to my sql database?
How do I present the initial state of checkboxes from the value in database? (Eg: 1 for checkbox is checked, 0 for unchecked)
(I am open to any ways of implementing the above requirement, so feel free to provide your solution other than the codes below)
Here is my code:
mysqlquerymodel.h
class MySqlQueryModel : public QSqlQueryModel
{
Q_OBJECT
public:
explicit MySqlQueryModel(QObject *parent = 0);
Qt::ItemFlags flags(const QModelIndex & index) const;
QVariant data(const QModelIndex & index, int role) const;
bool setData(const QModelIndex & index, const QVariant & value, int role);
QMap<int, Qt::CheckState> check_state_map;
};
mysqlquerymodel.cpp (ignore the qdebug its for checking)
MySqlQueryModel::MySqlQueryModel(QObject *parent) : QSqlQueryModel(parent), check_state_map()
{
}
Qt::ItemFlags MySqlQueryModel::flags(const QModelIndex & index) const
{
if (!index.isValid()) {
qDebug()<<("item1");
return nullptr;
}
if (index.column() == 2){
qDebug()<<("item2");
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
}
qDebug()<<("item3");
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
QVariant MySqlQueryModel::data(const QModelIndex & index, int role) const
{
if (!index.isValid()){
qDebug()<<("data1");
return QVariant();
}
if(role== Qt::CheckStateRole)
{
if(index.column() == 2)
{
if (check_state_map.contains(index.row())){
qDebug()<<("data2");
return check_state_map[index.row()] == Qt::Checked ? Qt::Checked : Qt::Unchecked;
}
qDebug()<<("data3");
return Qt::Unchecked;
}
}
return QSqlQueryModel::data(index,role);//!!!!!
}
bool MySqlQueryModel::setData(const QModelIndex & index, const QVariant & value, int role)
{
if(!index.isValid()){
qDebug()<<("set1");
return false;
}
if (role == Qt::CheckStateRole && index.column() == 2)
{
qDebug()<<("set2");
check_state_map[index.row()] = (value == Qt::Checked ? Qt::Checked : Qt::Unchecked);
}
qDebug()<<("set3");
return true;
}
I am trying to implement it in tawindow.ui
tawindow.cpp
//load table
void TAWindow::on_loadBtn_clicked(){
conn.connOpen();
QSqlQuery* qry = new QSqlQuery(conn.mydb);
qry->prepare("select * from students");
qry->exec();
tablemodel->setQuery(*qry);
ui->tableView->setModel(tablemodel);
conn.connClose();
}
//save table and update database
void TAWindow::on_saveBtn_clicked()
{
}
Thanks in advance.

you can use ui->combobox->ischecked(); to check if the comboBox is checked or not.
and to store the state of the comboBox you can use a variable.
Something like this :
QString var;
if(ui->comboBox->isChecked())
{
var = "Checked";
}
else
{
var = "Not Checked";
}
and store var in you database.

Related

Can't sort model by QDateTime with using QSortFilterProxyModel sort by role

I am stuck with simple problem but I can't figure it out why my model is not sorted.
I have SimpleModel class that inherits from QAbstractListModel and I want to sort it by DateTime role.
This is how I am setting the Proxy in my main.cpp:
SimpleModel m;
ProxyModel proxyModel;
proxyModel.setSourceModel(&m);
proxyModel.setSortRole(SimpleModel::SimpleRoles::DateTimeRole);
engine.rootContext()->setContextProperty("simpleModel", &proxyModel);
My Items in the SimpleModel are objects from SimpleItem class which has only Name and DateTime. This is my SimpleModel data method:
QVariant SimpleModel::data(const QModelIndex &index, int role) const
{
if(!index.isValid()) {
return QVariant();
}
auto simpleItem = static_cast<SimpleItem*>(index.internalPointer());
if (!simpleItem) {
return QVariant();
}
if(role == NameRole) {
return simpleItem->name();
}
//This is used for displaying in QML
else if(role == DateRole) {
return simpleItem->dateTime().toString("yyyy-MM-dd");
}
// This is used for dsiplaying in QML too
else if(role == TimeRole) {
return simpleItem->dateTime().toString("hh:mm");
}
// This Role is only used for sorting
else if(role == DateTimeRole) {
return simpleItem->dateTime();
}
return QVariant();
}
And in my ProxyModel that inherits from QSortFilterProxyModel class I have implemented lessThan() methood but it never gets called:
bool ProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
qDebug() << "called lessThan()";
QVariant leftData = sourceModel()->data(source_left);
QVariant rightData = sourceModel()->data(source_right);
if (leftData.userType() == QMetaType::QDateTime)
return leftData.toDateTime() < rightData.toDateTime();
return false;
}
What I am doing wrong or do you have any idea that I can try? If you need more code I will update.
Thanks in advance.
as you say
it works after calling proxyModel.sort(0, Qt::DescendingOrder); after setSortRole

Cannot check item of QListView

I'm working on a dialog class with a QListView and a customized model inheriting QAbstractListModel. Items on my list are custom widgets with several labels.
I managed to make a checkbox displayed for each item by reimplementing data(), setData() and flags() methods of my model, but when I run my code and click on a checkbox associated to one of the item, the checkbox doesn't appear as checked (remains unchecked).
Here's my code:
mymodel.h
class MyModel : public QAbstractListModel
{
Q_OBJECT
public:
MyModel(QObject *parent);
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE ;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole) Q_DECL_OVERRIDE;
Qt::ItemFlags flags(const QModelIndex & index) const Q_DECL_OVERRIDE ;
QSet<QPersistentModelIndex> checkedItems;
};
mymodel.cpp
QVariant MyModel::data(const QModelIndex &index, int role) const
{
if(role == Qt::CheckStateRole)
{
if(checkedItems.contains(index))
return Qt::Checked;
else
return Qt::Unchecked;
}
return QVariant();
}
bool MyModel::setData(const QModelIndex & index, const QVariant & value, int role)
{
if(role == Qt::CheckStateRole)
{
if(value == Qt::Checked)
checkedItems.insert(index);
else
checkedItems.remove(index);
emit dataChanged(index, index);
}
return true;
}
Qt::ItemFlags MyModel::flags(const QModelIndex &index) const
{
return QAbstractListModel::flags(index) | Qt::ItemIsUserCheckable;
}
mydelegate.h
class MyDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
MyDelegate(QObject* parent = 0) : QStyledItemDelegate(parent) {}
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
mydelegate.cpp
QSize MyDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
return QSize(400, 120);
}
In the constructor of mydialog.cpp
model = new MyModel(this);
ui->list->setModel(model);
ui->list->setItemDelegate(new MyDelegate(this));
I've tried adding flags Qt::ItemIsEnabled and Qt::ItemIsEditable but it didn't change anything.
I'm not very familiar with view/model implementation so far, although I've read Qt docs.
Thx for the help !
To be more precise about my comment, here is what I had in mind
bool MyModel::setData(const QModelIndex & index, const QVariant & value, int role)
{
if(role == Qt::CheckStateRole)
{
if(value == Qt::Checked)
checkedItems.insert(index);
self.checkBoxList[index.row()][index.column()].setChecked(True)
else
checkedItems.remove(index);
self.checkBoxList[index.row()][index.column()].setChecked(False)
emit dataChanged(index, index);
}
return true;
}
I just do not have all of your code so I do not know how your self.checkBoxList is done. Are the checkBox child of the table?

Size of editor in QItemDelegate

I have a custom Delegate, subclassed from QItemDelegate, that provides a QComboBox in the very first column and a QLineEdit in all other columns.
SensorDisplayDelegate::SensorDisplayDelegate(QObject *parent) :
QItemDelegate(parent)
{}
QWidget *SensorDisplayDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
int col = index.column();
if(col == 0)
{
QComboBox *comboBox = new QComboBox(parent);
connect(comboBox, SIGNAL(activated(int)), this, SLOT(setData(int)));
comboBox->setEditable(false);
//comboBox->setMaximumSize(editorSize);
comboBox->setInsertPolicy(QComboBox::NoInsert);
currentComboBox = comboBox;
return comboBox;
}
else
{
QLineEdit *lineEdit = new QLineEdit(parent);
return lineEdit;
}
return NULL;
}
void SensorDisplayDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
int col = index.column();
if(col == 0)
{
QComboBox *comboBox = static_cast<QComboBox*>(editor);
QStringList comboItems = index.data(Qt::EditRole).toStringList();
comboBox->addItem("Add New Sensor");
comboBox->addItems(comboItems);
QCompleter *completer = new QCompleter(comboItems);
completer->setCaseSensitivity(Qt::CaseInsensitive);
comboBox->setCompleter(completer);
comboBox->showPopup();
}
else
{
QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);
lineEdit->setText(index.data(Qt::EditRole).toString());
lineEdit->show();
}
}
void SensorDisplayDelegate::setModelData(QWidget *editor,
QAbstractItemModel *model,
const QModelIndex &index) const
{
int col = index.column();
if(col == 0)
{
QComboBox *comboBox = static_cast<QComboBox*>(editor);
if(comboBox->currentIndex() == 0)
emit addNewSensor();
else
emit populateSensorView(comboBox->currentText());
}
else
{
QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);
model->setData(index, QVariant(lineEdit->text()));
}
}
void SensorDisplayDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
{
editor->setGeometry(option.rect);
}
QSize SensorDisplayDelegate::sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
return editorSize;
}
void SensorDisplayDelegate::setData(int option)
{
emit commitData(currentComboBox);
emit closeEditor(currentComboBox);
}
The editTrigger has been set to selectClicked. I want the combo box to cover the entire cell in the QTableView. However, now it just appears as blip on the left hand corner. I tried setting the minimum size by passing the cell size through an event filter that listens for mousePressed on QTableView. However, the corresponding slot in the delegate is never called. Here's the code:
MultiEventFilter.cpp :
bool MultiEventFilter::eventFilter(QObject *obj, QEvent *event)
{
if(event->type() == QEvent::MouseButtonPress)
{
if(obj->objectName() == "sensorlocationTableView")
{
QTableView *sensorView = static_cast<QTableView*>(obj);
QModelIndexList idxs = sensorView->selectionModel()->selectedIndexes();
if(!idxs.empty())
{
QModelIndex idx = idxs.at(0);
emit passCellSize(QSize(sensorView->columnWidth(idx.column()),
sensorView->rowHeight(idx.row())));
}
}
}
return false;
}
installed on qApp.
MainWindow.cpp:
eFilter = new MultiEventFilter();
connect(eFilter, SIGNAL(passCellSize(QSize)),
sensor_display_delegate, SLOT(setEditorSize(QSize)));
SensorDisplayDelegate.cpp slot:
void SensorDisplayDelegate::setEditorSize(const QSize &size)
{
editorSize = size;
}
where QSize editorSize is a private member.
How can I set the size of the editor correctly? I need something general that can be applied to the QLineEdit editors as well.
Also, is it necessary to explicitly emit commitData() when the editor is closed? I have not seen this done in any example codes involving QComboBox.
I suspect your eventFilter is intercepting the click events before the selection indexes have been set. So, you're effectively always hitting an empty idxs IndexList?
Try replacing that loop with something like:
bool MultiEventFilter::eventFilter(QObject *obj, QEvent *event)
{
if(event->type() == QEvent::MouseButtonPress)
{
if(obj->objectName() == "sensorlocationTableView")
{
emit locationTableViewClicked();
}
}
return false;
}
....
connect(eFilter, SIGNAL(locationTableViewClicked()),
sensor_display_delegate, SLOT(setEditorSize()));
...
void SensorDisplayDelegate::setEditorSize()
{
QModelIndexList idxs = sensorView->selectionModel()->selectedIndexes();
if(!idxs.empty())
{
QModelIndex idx = idxs.at(0);
editorSize = QSize(sensorView->columnWidth(idx.column()),
sensorView->rowHeight(idx.row()));
}
}

Can't get QColumnView to display more than one row of data with my QAbstractItemModel-derived class

I'm implementing a custom model using QAbstractItemModel that I want to have displayed in a QColumnView. The data in the model is a simple hierarchy of directories and files, where each directory can contain files and other directories.
My QAbstractItemModel-derived class is called MyModel. It relies on two other QObject-derived classes, Directory and Item. Directory has a few methods for returning the number of items it contains (including subdirectories), getting an item at a particular index, and so on. For the purposes of this code so far, File just has a name and is contained by instances of Directory. MyModel also contains a method called invisibleTopLevelDirectory() that returns the Directory that is the root of this hierarchy.
The problem is that my QColumnView doesn't display the data correctly. Each column displays at most one row, regardless of how many rows it's supposed to have. I have no idea why! As near as I can tell I'm implemented my QAbstractItemModel-derived class correctly. Even more confounding is that if I use a QTreeView instead of a QColumnView, the tree view does display all of the data.
Here is the relevant code:
// Returns the instance of Directory at the given index, or NULL if the data
// is not a Directory instance.
Directory *directoryAtModelIndex(const QModelIndex &index)
{
return qobject_cast<Directory *>((QObject *)index.internalPointer());
}
// Returns the instance of File at the given index, or NULL if the data
// is not a File instance.
File *fileAtModelIndex(const QModelIndex &index)
{
return qobject_cast<File *>((QObject *)index.internalPointer());
}
QModelIndex MyModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent)) {
return QModelIndex();
}
Directory *parentDirectory;
if (!parent.isValid()) {
parentDirectory = invisibleTopLevelDirectory();
} else {
parentDirectory = (Directory *)(parent.internalPointer());
}
if (!parentDirectory) {
return QModelIndex();
}
QObject *item = parentDirectory->itemAtIndex(row);
if (item) {
return createIndex(row, column, item);
} else {
return QModelIndex();
}
}
QModelIndex MyModel::parent(const QModelIndex &index) const
{
if (!index.isValid()) {
return QModelIndex();
}
QObject *item = (QObject *)index.internalPointer();
Directory *parentItem = qobject_cast<Directory *>(item->parent());
if (!parentItem || parentItem == invisibleTopLevelDirectory()) {
return QModelIndex();
}
int listIndex = parentItem->indexOfItem(item);
if (listIndex < 0) {
return QModelIndex();
} else {
return createIndex(listIndex, index.column(), parentItem);
}
}
int MyModel::rowCount(const QModelIndex &parent) const
{
if (parent.column() > 0) {
return 0;
}
if (!parent.isValid()) {
return invisibleTopLevelDirectory() ? invisibleTopLevelDirectory()->itemCount() : 0;
}
Directory *parentItem = directoryAtModelIndex(parent);
if (parentItem) {
return parentItem->itemCount();
} else {
return 0;
}
}
int MyModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return 1;
}
QVariant MyModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
if (role == Qt::DisplayRole) {
if (Directory *item = directoryAtModelIndex(index)) {
return item->name;
} else if (File *item = fileAtModelIndex(index)) {
return item->name;
}
}
return QVariant();
}
I've poured over this code several times and I simply cannot figure out why my QColumnView does not ever display more than one row of data for any given column. I've used print statements to confirm that index() and data() are being called for rows passed the first, rowCount() is being called for all parent items and is returning the correct number of rows, and parent() is returning the correct row, column, and parent QModelIndex. This is particularly confirmed by the fact that if I use this same model in a QTreeView it works correctly.
Can anyone figure out what I'm doing wrong?
Error in method parent: instead of row of parent you take row of index. Shoud be:
Directory* parent_of_parent = qobject_cast<Directory*>(parentItem->parent());
if (!parent_of_parent) {
return QModelIndex();
}
int listIndex = parent_of_parent->indexOfItem(parentItem);
You may use QFileSystemModel.

Bind CheckBox checked-state in TableView to custom model attribute

I've got a QML-application containing a TableView with two columns. One of them is a CheckBox. Now I created a model derived from QAbstractTableModel. Reading data for the ordinary text-column already works but how do I sync the checked-property for my CheckBox with the model?
Currently I can't even set it checked via model. You find the relevant code below.
tablemodel.cpp
TableModel::TableModel(QObject *parent) :
QAbstractTableModel(parent)
{
list.append("test1");
list.append("test2");
}
int TableModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return list.count();
}
int TableModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return 2;
}
QVariant TableModel::data(const QModelIndex & index, int role) const {
if (index.row() < 0 || index.row() >= list.count())
return QVariant();
if (role == NameRole)
return list[index.row()]
else if (role== EnabledRole){
//list is not QList<QString>, its a custom class saving a String and a boolean for the checked state
return list[index.row()].isEnabled();
}
else {
return QVariant();
}
}
QHash<int, QByteArray> TableModel::roleNames() const {
QHash<int, QByteArray> roles;
roles[NameRole] = "name";
roles[EnabledRole] = "enabled";
return roles;
}
tablemodel.hpp
class TableModel : public QAbstractTableModel
{
Q_OBJECT
public:
enum Roles {
NameRole = Qt::UserRole + 1,
EnabledRole
};
explicit TableModel(QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex & parent = QModelIndex()) const;
Q_INVOKABLE QVariant data (const QModelIndex & index, int role) const;
protected:
QHash<int, QByteArray> roleNames() const;
private:
QList<QString> list;
main.qml
TableView {
id: Table
model: TableModel
TableViewColumn {
role: "name"
}
TableViewColumn {
role: "enabled"
delegate: CheckBox {
//how to get the right state from the model
//checked: ??
}
}
}
main.cpp
QQmlApplicationEngine engine;
QQmlContext * context = new QQmlContext(engine.rootContext());
TableModel tableModel;
context->setContextProperty("tableModel",&tableModel);
QQmlComponent component(&engine, QUrl("qrc:///main.qml"));
QQuickWindow * window = qobject_cast<QQuickWindow*>(component.create(context));
window->show();
You can emit signal from qml, when clicked on checkbox; connect this signal to your model slot and do something
main.qml
TableView {
id: table
objectName: "myTable"
signal checkedChanged(int index, bool cheked)
TableViewColumn {
role: "enabled"
delegate: CheckBox {
onClicked: table.checkedChanged(styleData.row, checked);
}
}
main.cpp
QQuickItem *obj = engine.rootObjects().at(0)->findChild<QQuickItem*>(QStringLiteral("myTable"));
QObject::connect(obj,SIGNAL(checkedChanged(int,bool)),tableModel,SLOT(mySlot(int,bool)));