Evgenii Legotckoi
Evgenii LegotckoiAug. 19, 2015, 3:59 a.m.

Qt/C++ - Lesson 008. QDataWidgetMapper - Working with the database via a widget

To display the data in a widget with an arbitrary use QDataMapperWidget class. To work with this widget is still required model for presentation.

For example, QSqlTableModel or QSqlRelationalTableModel , but the data is not already substituted QTableView , and various arbitrary objects. For example QLineEdit or QComboBox . Or in the dialog box to add records with which to work in this article.

Thus, the problem is as follows. Write a program that displays a list of computers in the table, and each computer has three data fields: Имя Хоста, IP-адрес и MAC-адрес. There should also be a button to display the dialog box, through which we can add a new device to the table. Also, we should be able to edit the records through the same dialog box.

Project structure of QDataWidgetMapper

The project is created as an application Qt Widgets, where the following files are created:

  • QDataMapperWidget.pro ;
  • mainwindow.h - header file of the main application window;
  • mainwindow.cpp - source code of window;
  • main.cpp - the main source file from which the application starts;
  • mainwindow.ui - form of the main application window;
  • database.h - header file of helper class to be used for information that is placed in a database;
  • database.cpp - source code of helper class file to be used for information that is placed in a database;
  • dialogadddevice.h - header file dialog to add or edit entries;
  • dialogadddevice.cpp - source file dialog box to add and edit entries;
  • dialogadddevice.ui

Note. Most of the interface is created in the designer, so as not to clutter up the main logic code superfluous information. In fact it is only a matter of taste and habit.


mainwindow.ui

Form of the main window is simple. And you will use from this form the two objects:

  • addDeviceButton
  • deviceTableView

dialogadddevice.ui

In the form of the dialog box there are three fields a QLineEdit, two buttons and one ButtonBox, which is part of the default class that inherits from QDialog class.

We have the following items:

  • HostnameLineEdit
  • IPAddressLineEdit
  • MACLineEdit
  • buttonBox
  • nextButton
  • previousButton

QDataMapperWidget.pro

The profile of the project need to add a directive that declares the use of libraries of SQL.

#-------------------------------------------------
#
# Project created by QtCreator 2015-08-16T23:58:29
#
#-------------------------------------------------

QT       += core
QT       += gui
QT       += sql

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = QDataMapperWidget
TEMPLATE = app


SOURCES += main.cpp\
        mainwindow.cpp \
    database.cpp \
    dialogadddevice.cpp

HEADERS  += mainwindow.h \
    database.h \
    dialogadddevice.h

FORMS    += mainwindow.ui \
    dialogadddevice.ui

main.cpp

The file used in the project, being created by default.

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

mainwindow.h

The header of the main window define slots to start the dialogue add entries, update the model and start dialogue editing entries. There are also methods for initializing the appearance of the window, in addition to the basic settings made in Qt Designer interface, as well as the method of initialization data representation model.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSqlTableModel>

#include <database.h>
#include <dialogadddevice.h>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_addDeviceButton_clicked();
    void slotUpdateModels();
    void slotEditRecord(QModelIndex index);

private:
    Ui::MainWindow              *ui;
    DataBase                    *db;
    QSqlTableModel              *modelDevice;

private:
    void setupModel(const QString &tableName, const QStringList &headers);
    void createUI();
};

#endif // MAINWINDOW_H

mainwindow.cpp

The source code of the main window initialization file data tables, as was done in a previous article, for example by working with QSqlTableModel . And also prescribes the logic behavior of the application by clicking the Add button, which causes the addition of recording the dialogue table. Also, this dialog is called by double-clicking on an entry in the data table. In this case, information transmitted in a dialogue on which recording has been pressed and its data are substituted into the field for editing.

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setWindowTitle("QDataWidgetMapper Example");
    /* The first step is to create an object for the database 
     * and initialize the database connection
     * */
    db = new DataBase();
    db->connectToDataBase();

    /* Initialize the model to represent the data indicating the names of the columns
     * */
    this->setupModel(DEVICE,
                     QStringList() << trUtf8("id")
                                         << trUtf8("Имя хоста")
                                         << trUtf8("IP адрес")
                                         << trUtf8("MAC-адрес")
               );
    this->createUI();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::setupModel(const QString &tableName, const QStringList &headers)
{
    modelDevice = new QSqlTableModel(this);
    modelDevice->setTable(tableName);
    modelDevice->select();
    /* Set the columns names in a table with sorted data
     * */
    for(int i = 0, j = 0; i < modelDevice->columnCount(); i++, j++){
        modelDevice->setHeaderData(i,Qt::Horizontal,headers[j]);
    }
}

void MainWindow::createUI()
{
    ui->deviceTableView->setModel(modelDevice);     
    ui->deviceTableView->setColumnHidden(0, true); 
    ui->deviceTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->deviceTableView->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->deviceTableView->resizeColumnsToContents();
    ui->deviceTableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->deviceTableView->horizontalHeader()->setStretchLastSection(true);

    connect(ui->deviceTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(slotEditRecord(QModelIndex)));
}

/* Method for activating dialogue of adding records
 * */
void MainWindow::on_addDeviceButton_clicked()
{
    /* Create a dialogue and connect it to signal the completion 
     * of the form slot refresh data representation model
     * */
    DialogAddDevice *addDeviceDialog = new DialogAddDevice();
    connect(addDeviceDialog, SIGNAL(signalReady()), this, SLOT(slotUpdateModels()));

    addDeviceDialog->setWindowTitle(trUtf8("Добавить Устройство"));
    addDeviceDialog->exec();
}

/* Slot of update data representation model
 * */
void MainWindow::slotUpdateModels()
{
    modelDevice->select();
}

/* Method for activating dialogue adding entries to the edit mode 
 * with the transmission of the selected row index
 * */
void MainWindow::slotEditRecord(QModelIndex index)
{

    DialogAddDevice *addDeviceDialog = new DialogAddDevice(index.row());
    connect(addDeviceDialog, SIGNAL(signalReady()), this, SLOT(slotUpdateModel()));

    addDeviceDialog->setWindowTitle(trUtf8("Редактировать Устройство"));
    addDeviceDialog->exec();
}

dialogadddevice.h

Header File dialog for adding and editing entries. As can be seen on the header file, there is also used a model to represent the data, but it does not broadcast data to a QTableView , such as in the tutorial on QSqlRelationalModel or MainWindow class of this article, and in QDataWidgetMapper object class. There is also overridden by accept() method, as before, close the window, you must make sure that the data is filled in correctly. In this project, the criterion of correctness of filling of data is the lack of duplicates.

#ifndef DIALOGADDDEVICE_H
#define DIALOGADDDEVICE_H

#include <QDialog>
#include <QSqlTableModel>
#include <QDataWidgetMapper>
#include <QMessageBox>

#include <database.h>

namespace Ui {
class DialogAddDevice;
}

class DialogAddDevice : public QDialog
{
    Q_OBJECT

public:
    explicit DialogAddDevice(int row = -1, QWidget *parent = 0);
    ~DialogAddDevice();

signals:
    void signalReady();

private slots:
    void on_buttonBox_accepted();
    void updateButtons(int row);

private:
    Ui::DialogAddDevice         *ui;
    QSqlTableModel              *model;
    QDataWidgetMapper           *mapper;

private:
    void setupModel();
    void createUI();
    void accept();
};

#endif // DIALOGADDDEVICE_H

dialogadddevice.cpp

In this file the logic of class was implemented. Checking the correctness of the entered data is carried out when you press the OK button. When properly filled fields produced record insert in the table and edit it. Validation of data in the IP адрес and MAC адрес is performed using a validator that writing has been described in a previous article . Also implemented on the verification record the existence of similar data.

#include "dialogadddevice.h"
#include "ui_dialogadddevice.h"

DialogAddDevice::DialogAddDevice(int row, QWidget *parent) :
    QDialog(parent),
    ui(new Ui::DialogAddDevice)
{
    ui->setupUi(this);

    /* Method for initializing the model, from which data will be transmitted
     * */
    setupModel();

    /* If the string is not specified, that is equal to -1, 
     * then the dialog works on the principle of creating a new record. 
     * Namely, in the model of the new line is inserted and the work is done with it.
     * */
    if(row == -1){
        model->insertRow(model->rowCount(QModelIndex()));
        mapper->toLast();
    /* Otherwise, the dialogue is tuned to a given record
     * */
    } else {
        mapper->setCurrentModelIndex(model->index(row,0));
    }

    createUI();
}

DialogAddDevice::~DialogAddDevice()
{
    delete ui;
}

void DialogAddDevice::setupModel()
{
    model = new QSqlTableModel(this);
    model->setTable(DEVICE);
    model->setEditStrategy(QSqlTableModel::OnManualSubmit);
    model->select();

    /* Initialize mapper and bind data fields to objects QLineEdit
     * */
    mapper = new QDataWidgetMapper();
    mapper->setModel(model);
    mapper->addMapping(ui->HostnameLineEdit, 1);
    mapper->addMapping(ui->IPAddressLineEdit, 2);
    mapper->addMapping(ui->MACLineEdit, 3);
    /* Manual confirmation of the change via the mapper
     * */
    mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);

    /* Connect "Connects" from the scroll buttons to prilistyvaniyu data model mapper
     * */
    connect(ui->previousButton, SIGNAL(clicked()), mapper, SLOT(toPrevious()));
    connect(ui->nextButton, SIGNAL(clicked()), mapper, SLOT(toNext()));
    /* When you change the index mapper to change the state of the buttons
     * */
    connect(mapper, SIGNAL(currentIndexChanged(int)), this, SLOT(updateButtons(int)));
}

/* The method for installing the validator to enter the IP and MAC addresses
 * */
void DialogAddDevice::createUI()
{
    QString ipRange = "(?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])";
    QRegExp ipRegex ("^" + ipRange
                     + "\\." + ipRange
                     + "\\." + ipRange
                     + "\\." + ipRange + "$");
    QRegExpValidator *ipValidator = new QRegExpValidator(ipRegex, this);
    ui->IPAddressLineEdit->setValidator(ipValidator);

    QString macRange = "(?:[0-9A-Fa-f][0-9A-Fa-f])";
    QRegExp macRegex ("^" + macRange
                      + "\\:" + macRange
                      + "\\:" + macRange
                      + "\\:" + macRange
                      + "\\:" + macRange
                      + "\\:" + macRange + "$");
    QRegExpValidator *macValidator = new QRegExpValidator(macRegex, this);
    ui->MACLineEdit->setValidator(macValidator);
}

void DialogAddDevice::on_buttonBox_accepted()
{
    /* SQL-query to check for the existence of records with the same credentials. 
     * If a record does not exist or is only editable index is currently recording, 
     * the dialog box allows you to record in a data table
     * */
    QSqlQuery query;
    QString str = QString("SELECT EXISTS (SELECT " DEVICE_HOSTNAME " FROM " DEVICE
                          " WHERE ( " DEVICE_HOSTNAME " = '%1' "
                          " OR " DEVICE_IP " = '%2' )"
                          " AND id NOT LIKE '%3' )")
            .arg(ui->HostnameLineEdit->text(),
                 ui->IPAddressLineEdit->text(),
                 model->data(model->index(mapper->currentIndex(),0), Qt::DisplayRole).toString());

    query.prepare(str);
    query.exec();
    query.next();

    /* If a record exists, the dialog is a warning message
     * */
    if(query.value(0) != 0){
        QMessageBox::information(this, trUtf8("Ошибка хоста"),
                                 trUtf8("Хост с таким именем или IP-адресом уже существует"));
    /* Otherwise, you are inserting new data into the table and the dialogue ends 
     * with the transmission of a signal to update the table in the main window
     * */
    } else {
        mapper->submit();
        model->submitAll();
        emit signalReady();
        this->close();
    }
}

void DialogAddDevice::accept()
{

}

void DialogAddDevice::updateButtons(int row)
{
    /* In that case, if we reach one of the extremes (the first or the last) of the indices 
     * in the data table, then we change the state 
     * of a corresponding button on the inactive state
     * */
    ui->previousButton->setEnabled(row > 0);
    ui->nextButton->setEnabled(row < model->rowCount() - 1);
}

database.h

Helper class to work with the database is a modified variant of the same class from the previous lessons.

#ifndef DATABASE_H
#define DATABASE_H

#include <QObject>
#include <QSql>
#include <QSqlQuery>
#include <QSqlError>
#include <QSqlDatabase>
#include <QFile>
#include <QDate>
#include <QDebug>

#define DATABASE_HOSTNAME   "ExampleDataBase"
#define DATABASE_NAME       "DataBase.db"

#define DEVICE                  "DeviceTable"
#define DEVICE_HOSTNAME         "Hostname"
#define DEVICE_IP               "IP"
#define DEVICE_MAC              "MAC"

class DataBase : public QObject
{
    Q_OBJECT
public:
    explicit DataBase(QObject *parent = 0);
    ~DataBase();

    void connectToDataBase();
    bool inserIntoDeviceTable(const QVariantList &data);

private:
    QSqlDatabase    db;

private:
    bool openDataBase();
    bool restoreDataBase();
    void closeDataBase();
    bool createDeviceTable();
};

#endif // DATABASE_H

database.cpp

#include "database.h"

DataBase::DataBase(QObject *parent) : QObject(parent)
{

}

DataBase::~DataBase()
{

}

void DataBase::connectToDataBase()
{
    if(!QFile("C:/example/" DATABASE_NAME).exists()){
        this->restoreDataBase();
    } else {
        this->openDataBase();
    }
}

bool DataBase::restoreDataBase()
{
    if(this->openDataBase()){
        if(!this->createDeviceTable()){
            return false;
        } else {
            return true;
        }
    } else {
        qDebug() << "Failed to restore the database";
        return false;
    }
    return false;
}

bool DataBase::openDataBase()
{
    db = QSqlDatabase::addDatabase("QSQLITE");
    db.setHostName(DATABASE_HOSTNAME);
    db.setDatabaseName("C:/example/" DATABASE_NAME);
    if(db.open()){
        return true;
    } else {
        return false;
    }
}

void DataBase::closeDataBase()
{
    db.close();
}

bool DataBase::createDeviceTable()
{
    QSqlQuery query;
    if(!query.exec( "CREATE TABLE " DEVICE " ("
                            "id INTEGER PRIMARY KEY AUTOINCREMENT, "
                            DEVICE_HOSTNAME  " VARCHAR(255)    NOT NULL,"
                            DEVICE_IP        " VARCHAR(16)     NOT NULL,"
                            DEVICE_MAC       " VARCHAR(18)     NOT NULL"
                        " )"
                    )){
        qDebug() << "DataBase: error of create " << DEVICE;
        qDebug() << query.lastError().text();
        return false;
    } else {
        return true;
    }
    return false;
}

bool DataBase::inserIntoDeviceTable(const QVariantList &data)
{
    QSqlQuery query;
    query.prepare("INSERT INTO " DEVICE " ( " DEVICE_HOSTNAME ", "
                                              DEVICE_IP ", "
                                              DEVICE_MAC " ) "
                  "VALUES (:Hostname, :IP, :MAC )");
    query.bindValue(":Hostname",    data[0].toString());
    query.bindValue(":IP",          data[1].toString());
    query.bindValue(":MAC",         data[2].toString());

    if(!query.exec()){
        qDebug() << "error insert into " << DEVICE;
        qDebug() << query.lastError().text();
        return false;
    } else {
        return true;
    }
    return false;
}

Result

Archive with source: QDataWidgetMapper

The result it should look like as follows:

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Do you like it? Share on social networks!

Ruslan Polupan
  • Feb. 13, 2017, 9:52 a.m.

Вопрос: Как записать данные в столбец который не привязан к элементу ввода в форме? Например в таблице

CREATE TABLE objects (
    object_id       INT(10) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
    brend_id        INT(10) unsigned NOT NULL,
    terminal_id     INT(10) unsigned NOT NULL,
    region_id       INT(10) unsigned NOT NULL,
    name            VARCHAR(100) NOT NULL,
    phone           VARCHAR(100)
);
привязаны к lineedit все поля кроме object_id и brend_id. object id это автоинкремент с ним все понятно подставляется значение по умолчанию. Как с используя QDataWidgetMapper записывать определенное значение в brend_id? При редактировании записей никаких вопросов нет а вот при попытке добавления выдает ошибку.
Evgenii Legotckoi
  • Feb. 13, 2017, 11:33 a.m.

Либо добавить в форум соответствующий виджет и сделать его скрытым через setVisible(false) , либо написать собственный маппер, в котором некоторые поля будут обрабатываться внутри маппера по заданному алгоритму. Я изучал вопрос маппера в QML, вот статья на эту тему , но там не для QSqlTableModel , а просто для модели данных.

Если делать со скрытым виджетом, то следует отслеживать сигнал currentIndexChanged и уже по нему обрабатывать требуемые скрытые виджеты так, как требуется. В документации нет и намёка на работу со скрытыми полями каким-либо документированным способом, поэтому вариант со скрытыми виджетами видится мне наиболее адекватным.

Ruslan Polupan
  • Feb. 14, 2017, 1:41 a.m.

Про скрытый виджет идея пришла сразу, но думал что есть другие варианты.

Evgenii Legotckoi
  • Feb. 14, 2017, 1:50 a.m.

Увы, но других вариантов я не вижу. QDataWidgetMapper явно подходит только для этого, иначе был бы какой-нибудь класс держатель значений. Но тот же самый QVariant, что наиболее близкое по смыслу, здесь явно не подходит. Так что... только скрытый виджет.

Ruslan Polupan
  • Feb. 14, 2017, 3:01 a.m.

Успешно заработало :-)

Evgenii Legotckoi
  • Feb. 14, 2017, 6:57 a.m.

Ну и отлично :-)

t
  • May 16, 2017, 3:08 p.m.

Как сделать так, что бы когда я открываю диалог, я не могу ничего делать в программе, пока я не закрою сам диалог? (Я не заметил(плохо прочел код))

Evgenii Legotckoi
  • May 16, 2017, 11:51 p.m.
QDialog dialog;
dialog.setModal(true);
dialog.show();
v
  • Feb. 12, 2018, 9:52 a.m.

Очень долго искал подобное описание.Спасибо. Только подскажи пожалуйста, как можно реализовать удаление строки с помощью кнопки

Evgenii Legotckoi
  • Feb. 12, 2018, 11:12 a.m.

Нужно навешать слот на клик кнопки

connect(ui->removeButton, &QPushButton::clicked, this, &MapperDialog::slotForRemove);
В этом слоте нужно достать id записи и по этому id удалить из базы данных запись, для этого можно реализовать вспомогательный метод в классе DataBase, который будет работать с QSqlQuery
bool DataBase::removeRecord(const int id)
{
    // Удаление строки из базы данных будет производитсья с помощью SQL-запроса
    QSqlQuery query;
 
    // Удаление производим по id записи, который передается в качестве аргумента функции
    query.prepare("DELETE FROM " TABLE " WHERE id= :ID ;");
    query.bindValue(":ID", id);
// Выполняем удаление
    if(!query.exec()){
        qDebug() << "error delete row " << TABLE;
        qDebug() << query.lastError().text();
        return false;
    } else {
        return true;
    }
}
v
  • Feb. 12, 2018, 6:20 p.m.

И еще, как можно из диалогового окна, из QTextEdit , передать данные в таблицу?

v
  • Feb. 12, 2018, 7:12 p.m.

Спасибо большое, но немного не пойму, что именно нужно прописать в слоте, что бы получать id и удалять через кнопку

Evgenii Legotckoi
  • Feb. 13, 2018, 3:01 a.m.

Вставка данных в таблицу базы данных производится через INSERT SQL запрос. Пример такого запроса есть в этой статье

У QTextEdi t есть метод text() который вернёт содержимое поле ввода QTextEdit, это содержимое будет представлено в виде QString, вот её и можно использовать в запросе INSERT.
Evgenii Legotckoi
  • Feb. 13, 2018, 3:05 a.m.

Можете в Mapper`e прикрутить ещё одно поле ввода, которое будет выводить id записи. Поле ввода можете настроить в режим readOnly, чтобы его нельзя было редактировать. Тогда сможете забирать через метод text() или value() данный id записи.

Ваши вопросы на данный момент имеют косвенное отношение к данной статье, поэтому задавайте эти вопросы, пожалуйста, на форуме сайта .
v
  • Feb. 13, 2018, 7:21 a.m.

Хорошо, спасибо большое за помощь

S
  • Feb. 15, 2018, 6:25 a.m.

Evileg, Здравствуйте. Можете пожалуйста пояснить, вот есть метод:

bool inserIntoDeviceTable(const QVariantList &data);
Как он используется? просто я не могу найти ни коннекта к нему, ни вызова где то :X
Evgenii Legotckoi
  • Feb. 15, 2018, 6:32 a.m.

Добрый день!

Вспомогательный класс у меня кочевал из статьи к статье... Поэтому некоторые методы используются не во всех статьях.
Так получилось, что конкретно этот метод не используется в данном примере.
s
  • Feb. 16, 2018, 5:31 p.m.
  • , а можете подсказать пожалуйста, как выполнить поиск по какой то колонке и выбрать найденную запись?
Evgenii Legotckoi
  • Feb. 18, 2018, 7:27 a.m.
QDataWidgetMapper работает на основе QSqlTableModel , следовательно вы можете пройтись в цикле по всем строкам модели данных и найти строку с нужными параметрами. Далее получается по номеру строки QModelIndex , который нужно передать в QDataWidgetMapper через метод setCurrentIndex . Тогда вы установить текущей записью нужную Вам строку.
k
  • March 21, 2018, 8:32 p.m.

А где находится связь между нажатием на кнопку "ok" и выполнением слота

on_addDeviceButton_clicked? В форме dialogadddevice.ui её тоже вроде бы нет. и не понятна надобность перегрузки слота DialogAddDevice::accept().
Evgenii Legotckoi
  • March 22, 2018, 4:19 a.m.
on_addDeviceButton_clicked добавляется через графический дизайнер, а подключение этого слота осуществляется по его имени в автоматически генерируемом файле ui_dialogadddevice.h. С перегрузкой метода accept() скорее мой недосмотр, сначала хотел использовать этот метод, а потом всё-таки воспользовался слотом on_addDeviceButton_clicked , а код до конца не подчистил, но как именно это происходило... уже не помню.
k
  • March 23, 2018, 10:32 a.m.

Спасибо!

v
  • March 31, 2018, 3:47 a.m.

Здравствуйте! В этой статье и нескольких Ваших других заметил метод closeDataBase(), но не нашел чтобы он использовался. И вообще во многих источниках видел исходники без закрытия БД. В php обычно (всегда) после запроса(ов) БД закрывается, и выходит здесь (в qt c++) также такое предусмотрено, но не совсем ясно понял когда и где его нужно (целесообразно) использовать? Как быть, если программа обращается к БД редко единичным образом или же когда все время требуется соединение и частые обращения?

Ruslan Polupan
  • March 31, 2018, 4:21 a.m.

В Qt После создания соединения с БД оно хранится на протяжения работы с приложением, но не постоянно. Необходимости закрывать соединение особо нет.

Evgenii Legotckoi
  • March 31, 2018, 8:01 a.m.

Добрый день!

Если это локальная база данных SQLite, которая используется только для данного локального приложения, то и смысла нет закрывать соединение.
Если используется множество соединений, или соединение по сети, то, полагаю, что целесообразно закрывать соединение с базой данных.
a
  • May 8, 2018, 9:29 a.m.
Thanks for the article.
I've been working around a problem with Qt/C++ for quite a while now
and I can't seem to find a solution neither in the official Qt documentation or in StackOverflow.
I'd like to show a QCheckBox with an associated text inside a general QWidget (say e.g. QComboBox), while also storing
their state in a model, using the Model/View paradigm.
Note that I obviously know how to create a QCheckBox with text (QCheckBox* checkBox = new QCheckBox(text);), and
that I have subclassed QAbstractTableModel in my custom model. The columns of the model are two: a QString for the
text associated with the checkbox and a bool to identify the checkbox status (checked/unchecked which gets mapped to Qt::Checked and Qt::Unchecked).
I already have a QList containing 4 labels to show with their "checked" status (which is, by default, unchecked).
I have already stumbled upon QDataWidgetMapper, but I don't think it suit my needs,
since I need to display a finite amount of rows at once, and I don't need to have "previous" and "next" functionality
like in the official example (http://doc.qt.io/qt-5/qtwidgets-itemviews-combowidgetmapper-example.html). But maybe I'm misunderstanding the power of QDataWidgetMapper.
Can you please help me understand what to do?

a
  • May 17, 2018, 9:21 a.m.

@EVILEG can you kindly provide a response? Please

Evgenii Legotckoi
  • May 17, 2018, 4:22 p.m.

If you want to show two columns from your model in combobox, then, I think, you can try to set Model in QComboBox and use custom ItemDelegate for QComboBox. Try to research information about delegates in official documentation.

s
  • July 9, 2019, 4:17 a.m.

А как можно добавить картинку в БД через QDataWidgetMapper? Это делается методом addMapping? Статью про изображение в базе данных я читал.

Evgenii Legotckoi
  • July 9, 2019, 4:41 a.m.

Добрый день. Я не проверял mapping изображений. Тем более, что вопрос в том, на что именно их маппить. В той статье делается на QLabel. А это нередактируемый виджет, как например QLineEdit. Тем более, что такой специфичный функционал как изображения вряд ли подготовливался для маппинга.

Так что здесь скорее при выборе записи нужно или делать дополнительный запрос к базе данных или смотреть, содержит ли текущая запись информацию об изображении и помещать его на QLabel. То есть прямой маппинг не заработает, по моему мнению.

s
  • July 9, 2019, 8:55 a.m.

Мне просто надо изображение из QLabel'a, расположеннго на диалоговом окне, вставить в БД. Значит, всё-таки это лучше сделать через запрос?

Evgenii Legotckoi
  • July 9, 2019, 9:02 a.m.

Лучше через запрос. Видите ли, если вы будете вставлять изображение в базу данных, там Вам придётся преобразовать данные изображения в BLOB. Как в той статье. А в QDataWidgetMapper такая логика просто отсутсвует. Так что здесь или наследоваться от QDataWidgetMapper и писать свой, или просто через запрос.

Через запрос проще и быстрее. Через наследование уже больше мороки, но в крупных проектах это окупается (В ОЧЕНЬ КРУПНЫХ)

A
  • May 17, 2020, 12:05 a.m.

Реализовал поиск через QSortFilterProxyModel, но при выборе результата в Mapper передаются значения из tableView по порядку, а не полученные в результате поиска значения. Такая же проблема при сортировке данных в tableView.
Как правильно реализовать поиск?

void dok::on_lineEdit_textChanged(const QString &arg1)
{
    proxySearch = new QSortFilterProxyModel(this);
    proxySearch->setSourceModel(modelIs);
    proxySearch->setFilterCaseSensitivity(Qt::CaseInsensitive);
    proxySearch->setFilterKeyColumn(-1);
    proxySearch->setFilterFixedString(arg1);
    ui->tableView_dok->setModel(proxySearch);
}
Evgenii Legotckoi
  • May 18, 2020, 3:24 a.m.

Для фильтрации QSqlTableModel нужно использовать метод setFilter у этой самой модели QSqlTableModel. Туда передаётся SQL запрос для where

AB
  • April 7, 2021, 9:25 a.m.

Начал делать что-то похожее, но без sql.
Не очень понимаю, что делает

mapper->setCurrentModelIndex(model->index(row,0));

Вернее не понимаю вот что: откуда он берёт данные, которые затем подставляет в диалог?

Моё собственное решение (делаю через QStandartItemModel) — передать в конструктор диалога все соответствующие поля из моей таблицы, которые затем через setText() прописать в QLineEdit.
Работать — работает (значения, разумеется, подставляются), но правильно ли так делать?

AB
  • April 7, 2021, 2:41 p.m.

Понял сам так - происходит через setTable() "Sets the database table on which the model operates to tableName. Does not select data from the table, but fetches its field information", а затем через select() "Populates the model with data from the table that was set via setTable(), using the specified filter and sort condition, and returns true if successful; otherwise returns false".

MP
  • Aug. 22, 2021, 1:26 p.m.
  • (edited)

Hi EVILEG,

Thnaks for example.
With this example code i am not getting header data. Any idea? I am new to QT and C++.

Can someone please help.

Comments

Only authorized users can post comments.
Please, Log in or Sign up
e
  • ehot
  • March 31, 2024, 11:29 a.m.

C++ - Тест 003. Условия и циклы

  • Result:78points,
  • Rating points2
B

C++ - Test 002. Constants

  • Result:16points,
  • Rating points-10
B

C++ - Test 001. The first program and data types

  • Result:46points,
  • Rating points-6
Last comments
k
kmssrFeb. 8, 2024, 3:43 p.m.
Qt Linux - Lesson 001. Autorun Qt application under Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lesson 007. Working with ICMP Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVADec. 25, 2023, 7:30 a.m.
Boost - static linking in CMake project under Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJoDec. 25, 2023, 5:38 a.m.
Boost - static linking in CMake project under Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
GvozdikDec. 18, 2023, 6:01 p.m.
Qt/C++ - Lesson 056. Connecting the Boost library in Qt for MinGW and MSVC compilers Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Now discuss on the forum
a
a_vlasovApril 14, 2024, 3:41 a.m.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел ДорофеевApril 13, 2024, 11:35 p.m.
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrexApril 4, 2024, 1:47 a.m.
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…
AC
Alexandru CodreanuJan. 19, 2024, 8:57 a.m.
QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…

Follow us in social networks