Russian Qt Forum
Март 28, 2024, 15:00 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Combobox и БД  (Прочитано 4402 раз)
Nabokov
Новичок

Offline Offline

Сообщений: 5


Просмотр профиля
« : Июнь 06, 2020, 17:39 »

Доброго времени суток, уважаемые форумчане. Qt начал заниматься недавно. Разрабатываю приложение для ведения расходов.
В моем приложении используется БД SQLITE. В ней есть следующая таблица:

Это Названия категорий расходов. Id - внешний ключ.
На графическом интерфейсе у меня есть Combobox. В него я заношу имена категорий таким образом:
1) Создаю объект QSqlQuery и выполняю запрос в БД на выборку всех имен продуктов.
2) Далее с помощью addItem добавляю это в Combobox.

Далее, в некотором месте программы я хочу получить Id категории в соответствии с тем, что в данный момент выбрано в combobox'е. И вот тут из-за того, что в Qt я новичок, я не совсем понимаю, как грамотно это сделать. В голову приходят следующие идеи:
1) Выполнять SQL-запрос на поиск Id категории по ее имени.
2) Либо где-то в таблице хранить map<Name,Id>. И в нужный момент получить нужный Id по имени.
Очевидно, что оба эти подхода плохие, хочется какого-то более элегантного решения.
Т.е., грубо говоря, чтобы я мог написать ui->combobox->currentText() и получить не имя категории, а именно ее Id.
Надеюсь на вашу помощь.
Записан
Nabokov
Новичок

Offline Offline

Сообщений: 5


Просмотр профиля
« Ответ #1 : Июнь 06, 2020, 19:29 »

После поисков ответов в интернете наткнулся вот на что:
Вроде бы в таких ситуациях, когда надо в комбобоксе отображать данные из БД, используют QSqlQueryModel (как вариант). Я реализовал следующий код:
Код:
    model = new QSqlQueryModel();
    model->setQuery(QString("SELECT -1 as Id, '(Выберите категорию)' as Name UNION SELECT Id, Name FROM Cat WHERE Cat.Art=%1").arg(articleId));
    ui->chooseCategory->setModel(model);
    ui->chooseCategory->setModelColumn(1);
Результатом запроса "SELECT -1 as Id, '(Выберите категорию)' as Name UNION SELECT Id, Name FROM Cat WHERE Cat.Art=%1").arg(articleId)" является два столбца: Id и Name. После того как я вызвал setModelColumn(1), в комбобоксе отображается Name. Теперь, когда мне надо получить Id, я делаю так:

Код:
ui->chooseCategory->setModelColumn(0);
    int Id = ui->chooseCategory->currentText().toInt();
    ui->chooseCategory->setModelColumn(1);
То есть сначала устанавливаю в модели нулевой столбец, получаю нужный индекс и меняю обратно. Задача выполнена, но однако мне все равно кажется, что это какой-то костыль, и требуемый Id можно получить вообще в одну строчку. Так что вопрос остается открытым)
Записан
demal
Чайник
*
Offline Offline

Сообщений: 53


Просмотр профиля
« Ответ #2 : Июнь 06, 2020, 21:54 »

это подключение QComboBox к БД
model=new QSqlTableModel;
    model->setTable("lichdel");
    model->setSort(2,Qt::AscendingOrder);
    ui->Fam->setModel(model);
    ui->Fam->setModelColumn(2);

А это получение значение другого столбца
ui->Nam->setText(model->record(ui->Fam->currentIndex()).value(3).toString());

Записан
tamplier
Новичок

Offline Offline

Сообщений: 40


Просмотр профиля
« Ответ #3 : Июнь 07, 2020, 07:34 »

Доброго времени суток, уважаемые форумчане. Qt начал заниматься недавно. Разрабатываю приложение для ведения расходов.
В моем приложении используется БД SQLITE. В ней есть следующая таблица:

Это Названия категорий расходов. Id - внешний ключ.
На графическом интерфейсе у меня есть Combobox. В него я заношу имена категорий таким образом:
1) Создаю объект QSqlQuery и выполняю запрос в БД на выборку всех имен продуктов.
2) Далее с помощью addItem добавляю это в Combobox.

Далее, в некотором месте программы я хочу получить Id категории в соответствии с тем, что в данный момент выбрано в combobox'е. И вот тут из-за того, что в Qt я новичок, я не совсем понимаю, как грамотно это сделать. В голову приходят следующие идеи:
1) Выполнять SQL-запрос на поиск Id категории по ее имени.
2) Либо где-то в таблице хранить map<Name,Id>. И в нужный момент получить нужный Id по имени.
Очевидно, что оба эти подхода плохие, хочется какого-то более элегантного решения.
Т.е., грубо говоря, чтобы я мог написать ui->combobox->currentText() и получить не имя категории, а именно ее Id.
Надеюсь на вашу помощь.

Постоянно выполнять из программы SQL-запросы, это не очень хорошая идея, тем более всё можно реализовать только средствами Qt (выполнив прежде один запрос к БД). Я бы работал не с моделью в данном случае (если база небольшая) а с QSqlQuery. Выполняешь запрос с необходимыми для задачи полями, "помещаешь" его в объект QSqlQuery. Метод next() для итерирования (прохода) по всем строкам запроса и методы: value(int), value(const QString &), для выбора нужного поля в таблице БД. Результат запроса можно поместить в QMultiHash, например (на случай совпадений в базе значений поля Name). И далее работать с хэш-таблицей...
Записан
tamplier
Новичок

Offline Offline

Сообщений: 40


Просмотр профиля
« Ответ #4 : Июнь 07, 2020, 08:23 »

Со старого проекта дернул подключение к БД и накидал вариант решения задачи с использованием QMultiHash ("Multi" на случай, если значения поля Name в таблице повторяются).
С помощью QtDesigner создал виджет и "кинул" на него QComboBox и QPushButton.


widget.h:
Код
C++ (Qt)
#ifndef WIDGET_H
#define WIDGET_H
 
#include <QWidget>
#include <QMultiHash>
 
class QSqlQuery;
 
namespace Ui {
 class Widget;
}
 
class Widget : public QWidget
{
 Q_OBJECT
 
public:
 bool createConnection();
 explicit Widget(QWidget *parent = 0);
 ~Widget();
 
private slots:
 void on_pushButton_clicked();
 
private:
 Ui::Widget *ui;
 QSqlQuery *query;
 QMultiHash<QString, int> *queryHash;
 
};
 
#endif // WIDGET_H
 


widget.cpp:
Код
C++ (Qt)
#include <QtWidgets>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QMultiHash>
#include "widget.h"
#include "ui_widget.h"
 
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
 
 ui->setupUi(this);
 
 if(!createConnection()) {
   // connection don't create
   return;
 }
 
 query = new QSqlQuery;
 
 QString strQuery =  "SELECT Id, Art, Name "
                     "FROM MyTable ";
 
 query->setForwardOnly(true); // faster
 
 if(!query->exec(strQuery)) {
   QMessageBox::critical(this, "Error",
                                         QString("Query error")
                                             + query->lastError().driverText() + "; "
                                             + query->lastError().databaseText());
   return;
 }
 
 queryHash = new QMultiHash<QString, int>();
 // insert items to queryHash and comboBox from field "Name"
 while(query->next()) {
   queryHash->insert(query->value("Name").toString(), query->value("Id").toInt());
 
   ui->comboBox->addItem(QString(query->value("Name").toString()));
 }
 
}
 
bool Widget::createConnection() {
 // create connection to DB
 QSqlDatabase db = QSqlDatabase::addDatabase("QODBC3");
 
 QString host = "SERVER\\SQL2008SRV";
 QString dataBase= "MyBase";
 QString userName = "sa";
 QString password = "password";
 
 db.setDatabaseName(QString("Driver={SQL Server};"
                     "Server=%1;Database=%2; Persist Security Info=true;"
                     "uid=%3;pwd=%4").arg(host).arg(dataBase).arg(userName).arg(password));
 
 if(!db.open()) {
   QMessageBox::critical(this, "Error",
                         QString("Connection error"));
 
   return false;
 }
 
 
 return true;
}
 
 
void Widget::on_pushButton_clicked() {
 
  if(queryHash->contains(ui->comboBox->currentText())) {
 
     QMessageBox::information(this,
                               "Value",
                               QString("Id = <b>" +
                               QString().setNum(queryHash->value(ui->comboBox->currentText())) + "</b>")
                              );
  }
}
 
 
Widget::~Widget()
{
 delete ui;
 delete query;
 delete queryHash;
}
 



main.cpp:
Код
C++ (Qt)
#include "widget.h"
#include <QApplication>
 
int main(int argc, char *argv[])
{
 QApplication a(argc, argv);
 Widget w;
 w.show();
 
 return a.exec();
}
 



Нужный результат выдается в слоте on_pushButton_clicked() при сигнале clicked() кнопки pushButton.
Код
C++ (Qt)
void Widget::on_pushButton_clicked() {
 
  if(queryHash->contains(ui->comboBox->currentText())) {
 
     QMessageBox::information(this,
                               "Value",
                               QString("Id = <b>" +
                               QString().setNum(queryHash->value(ui->comboBox->currentText())) + "</b>")
                              );
  }
}
 


P.S.: Единственное не сделал проверку на повторяющиеся значения. Надо будет использовать метод QHash::values(), который возвращают QList(). И уже с этим списком работать... В данном случае будет возвращено только одно значение Id (последнее в QMultiHash). Если точно повторений в таблице нет, то достаточно метода QHash::value(). И совет от новичка в Qt новичку: старайся почаще заглядывать в справку, сначала будет сложно разобраться, придется все-равно часто обращаться к интернету, но со временем станет легче разбираться с материалом и это скажется на быстроте кодинга и эффективности. И не забывай смотреть методы родителей класса, там много интересного Улыбающийся
И еще, советую простые окна (формы) создавать вручную (без QtDesigner) так зачастую проще и меньше лишнего кода. Наследуешься от QWidget, создаешь нужные элементы управления, создаешь нужный Layout, добавляешь в него эти элементы управления и устанавливаешь Layout на данный виджет.
Записан
Nabokov
Новичок

Offline Offline

Сообщений: 5


Просмотр профиля
« Ответ #5 : Июнь 07, 2020, 11:58 »

ui->Nam->setText(model->record(ui->Fam->currentIndex()).value(3).toString());
То что нужно, спасибо)
Записан
Nabokov
Новичок

Offline Offline

Сообщений: 5


Просмотр профиля
« Ответ #6 : Июнь 07, 2020, 12:04 »

Со старого проекта дернул подключение к БД и накидал вариант решения задачи с использованием QMultiHash ("Multi" на случай, если значения поля Name в таблице повторяются).
С помощью QtDesigner создал виджет и "кинул" на него QComboBox и QPushButton.


P.S.: Единственное не сделал проверку на повторяющиеся значения. Надо будет использовать метод QHash::values(), который возвращают QList(). И уже с этим списком работать... В данном случае будет возвращено только одно значение Id (последнее в QMultiHash). Если точно повторений в таблице нет, то достаточно метода QHash::value(). И совет от новичка в Qt новичку: старайся почаще заглядывать в справку, сначала будет сложно разобраться, придется все-равно часто обращаться к интернету, но со временем станет легче разбираться с материалом и это скажется на быстроте кодинга и эффективности. И не забывай смотреть методы родителей класса, там много интересного Улыбающийся
И еще, советую простые окна (формы) создавать вручную (без QtDesigner) так зачастую проще и меньше лишнего кода. Наследуешься от QWidget, создаешь нужные элементы управления, создаешь нужный Layout, добавляешь в него эти элементы управления и устанавливаешь Layout на данный виджет.
Спасибо за помощь, но мне все-таки не нравится вариант с хранением пары <QString, int> в каком-либо контейнере. Это требует написания лишнего кода, например, как вы уже отметили, в случае, если значения могут повторяться.
Я воспользовался вариантом, который написал demal:
Код:
model = new QSqlQueryModel();
model->setQuery(QString("SELECT -1 as Id, '(Выберите категорию)' as Name UNION SELECT Id, Name FROM Cat WHERE Cat.Art=%1").arg(articleId));
ui->chooseCategory->setModel(model);
ui->chooseCategory->setModelColumn(1);//В комбобоксе отображается содержимое столбца Name
//Если нужно получить Id:
int id = model->record(ui->chooseCategory->currentIndex()).value(0).toInt();
Все просто и лаконично, как мне кажется. К БД обращаемся всего один раз)
Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #7 : Июнь 07, 2020, 12:19 »

Модель кеширует данные сама и не лезет в БД каждый раз.
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
Nabokov
Новичок

Offline Offline

Сообщений: 5


Просмотр профиля
« Ответ #8 : Июнь 07, 2020, 13:20 »

Модель кеширует данные сама и не лезет в БД каждый раз.
Простите, не очень понимаю, на что вы намекаете. Типо можно вообще setQuery не делать?
Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #9 : Июнь 07, 2020, 13:49 »

QSqlQueryModel кеширует данные внути себя. То есть, хранит локально данные и запрашивает из из БД только один раз. На сколько я помню.
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
tamplier
Новичок

Offline Offline

Сообщений: 40


Просмотр профиля
« Ответ #10 : Июнь 07, 2020, 14:20 »

Со старого проекта дернул подключение к БД и накидал вариант решения задачи с использованием QMultiHash ("Multi" на случай, если значения поля Name в таблице повторяются).
С помощью QtDesigner создал виджет и "кинул" на него QComboBox и QPushButton.


P.S.: Единственное не сделал проверку на повторяющиеся значения. Надо будет использовать метод QHash::values(), который возвращают QList(). И уже с этим списком работать... В данном случае будет возвращено только одно значение Id (последнее в QMultiHash). Если точно повторений в таблице нет, то достаточно метода QHash::value(). И совет от новичка в Qt новичку: старайся почаще заглядывать в справку, сначала будет сложно разобраться, придется все-равно часто обращаться к интернету, но со временем станет легче разбираться с материалом и это скажется на быстроте кодинга и эффективности. И не забывай смотреть методы родителей класса, там много интересного Улыбающийся
И еще, советую простые окна (формы) создавать вручную (без QtDesigner) так зачастую проще и меньше лишнего кода. Наследуешься от QWidget, создаешь нужные элементы управления, создаешь нужный Layout, добавляешь в него эти элементы управления и устанавливаешь Layout на данный виджет.
Спасибо за помощь, но мне все-таки не нравится вариант с хранением пары <QString, int> в каком-либо контейнере. Это требует написания лишнего кода, например, как вы уже отметили, в случае, если значения могут повторяться.
Я воспользовался вариантом, который написал demal:
Код:
model = new QSqlQueryModel();
model->setQuery(QString("SELECT -1 as Id, '(Выберите категорию)' as Name UNION SELECT Id, Name FROM Cat WHERE Cat.Art=%1").arg(articleId));
ui->chooseCategory->setModel(model);
ui->chooseCategory->setModelColumn(1);//В комбобоксе отображается содержимое столбца Name
//Если нужно получить Id:
int id = model->record(ui->chooseCategory->currentIndex()).value(0).toInt();
Все просто и лаконично, как мне кажется. К БД обращаемся всего один раз)

Пожалуйста. Если вариант demal подходит больше и код меньше, то он лучший. Просто, как правило, технология "модель - представление" используется для "больших" данных, а в вашем случае, как я понял, их немного (раз помещаете результат запроса в QComboBox). Но самое главное чтобы код корректно и быстро работал, и был достаточно лаконичен  Улыбающийся
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.149 секунд. Запросов: 22.