В этой статье мы напишем простую программу с использованием библиотеки SQLite. Программа создаст базу данных, запишет туда информацию, а потом выведет эту информацию на экран.
1. Создание нового проекта
Для работы с таблицей мы будем виджеты GTK+, поэтому запустите CodeBlocks и выполните «Файл — Создать — Проект — GTK+ project». Готовый проект вы можете скачать в конце этой статьи.
Установите параметры проекта. Меню «Проект — Свойства — Цели сборки». Поставьте выделенные параметры.
Запустите приложение для проверки.
Библиотека GTK+ работает. Теперь убедитесь, что библиотека SQLite подключена к CodeBlocks. Об этом подробно написано здесь.
2. Подключение GtkListStore для вывода таблицы
В GTK+ для работы со сложными данными используется GtkTreeModel — набор функций для хранения и отображения данных.
Существует две модели хранения GtkListStore и GtkTreeStore и один виджет отображения GtkTreeView, который поддерживает обе модели хранения.
Для работы с таблицами нужно сделать запрос к БД и поместить результат в структуру GtkListStore. Виджет GtkTreeView отобразит данные в виде таблицы.
Для работы с таблицей в GTK+ нужно:
- Создать буфер GtkListStore.
- Добавить строки в буфер.
- Создать таблицу GtkTreeView.
- Добавить столбцы в таблицу.
Для обращения к таблице используется:
- Итератор GtkTreeIter — текущая строка.
- Колонка GtkTreeViewColumn — текущий столбец.
- Рендер GtkCellRenderer — текущая ячейка.
Запускаем Glade и готовим виджет, для этого.
- Создаем буфер liststore1 для модели таблицы,
- Создаем виджет дерева treeview1.
- Указываем для этого виджета буфер liststore1.
Сохраняем интерфейс в файл main.glade. Виджет готов, теперь его надо настроить: указать столбцы и формат ячеек.
3. Настройка виджета GtkTreeView в Glade
У нас сейчас пустое окно, в котором есть дерево treeview1 и буфер liststore1. Нажимаем на иконку Edit.
В редакторе виджета «Дерево» столбцы настраиваются на вкладке “Основные”, а ячейки — на вкладке “Иерархия”. Сначала добавим три столбца:
- guint1
- gcharray1
- guin2
Теперь настроим ячейки:
- Номер
- Фамилия
- Оклад
Всё, интерфейс готов. Теперь можно писать обращение к базе данных.
4. Создание базы данных SQLite
Для подключения к БД SQLite используется команда sqlite3_open(«b1.db», &db). В этой строчке открывается база данных b1.db, если ее нет, то она создается.
В качестве примера возьмем таблицу «Сотрудники», в которой зададим поля:
- N
- Фамилия
- Оклад
Теперь составляем SQL-запрос, в котором создаем таблицу «Сотрудники» и задаем значения полей. Начинается запрос с удаления таблицы «Сотрудники», если она есть. Это для того, чтобы мы могли запускать программу несколько раз.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// Запись в БД int SaveDB() { char* SQL1 = "DROP TABLE IF EXISTS Сотрудники; \ CREATE TABLE Сотрудники(N,Фамилия,Оклад); \ INSERT INTO Сотрудники VALUES(1,'Иванов',20000); \ INSERT INTO Сотрудники VALUES(2,'Петров',25000); \ INSERT INTO Сотрудники VALUES(3,'Сидоров',30000); \ "; if (sqlite3_exec(db, SQL1, 0, 0, &err)) { sprintf(str, "Ошибка выполнения SQL-запроса: %sn", err); sqlite3_free(err); return 2; } return 0; } |
5. Загрузка данных в буфер виджета GtkTreeView
Когда мы подключились к баз данных, мы можем загрузить строки БД в буфер виджета GtkTreeView. Сам буфер мы задаем строчкой:
1 |
GtkListStore *buffer; // Буфер таблицы |
Загрузку данных выполняет следующий код:
1 2 3 4 5 6 7 8 9 10 |
// Цикл по SQL-запросу и запись в буфер таблицы while((rc = sqlite3_step(stmt)) == SQLITE_ROW) { gtk_list_store_append (buffer, &iter); gtk_list_store_set (buffer, &iter, C_NUM, sqlite3_column_int(stmt, C_NUM), C_NAME, sqlite3_column_text(stmt, C_NAME), C_OKLAD, (guint) sqlite3_column_int(stmt, C_OKLAD), -1); } |
Здесь мы в цикле перебираем строки запроса и заполняем ячейки таблицы значениями полей из базы данных.
6. Итоговая программа
Остальные шаги по выводу таблицы на экран достаточно стандартны. Подключаем интерфейс main.glade и выводим на экран основное окно. Вот код целиком.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
/**************************************** Пример использования SQLite и GtkTreeView *****************************************/ #include <stdlib.h> #include <gtk/gtk.h> #include <sqlite3.h> GtkWidget *window1; GtkBuilder *builder1; sqlite3 *db; // хэндл БД // Структуры данных для виджета GtkListStore *buffer; // Буфер таблицы GtkTreeView *table; // Таблица GtkTreeViewColumn *column; // Отображаемая колонка GtkTreeIter iter; // Итератор таблицы (текущая строка) GtkCellRenderer *renderer; // Рендер таблицы (текущая ячейка) // Обозначения полей enum { C_NUM, C_NAME, C_OKLAD }; // Структуры данных для базы данных sqlite3_stmt* stmt; // строка запроса к БД char *err = 0; int rc = 0; int ShowGladeError() { GtkWidget *dialog; dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "Не найден файл\n main.glade"); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); return 0; } char str[1000]; int lenstr=0; // Открываем БД void StartDB() { if( sqlite3_open("b1.db", &db)) { sprintf(str, "Ошибка открытия БД: %s\n", sqlite3_errmsg(db)); } } void EndDB() { sqlite3_close(db); } // Запись в БД int SaveDB() { char* SQL1 = "DROP TABLE IF EXISTS Сотрудники; \ CREATE TABLE Сотрудники(N,Фамилия,Оклад); \ INSERT INTO Сотрудники VALUES(1,'Иванов',20000); \ INSERT INTO Сотрудники VALUES(2,'Петров',25000); \ INSERT INTO Сотрудники VALUES(3,'Сидоров',30000); \ "; if (sqlite3_exec(db, SQL1, 0, 0, &err)) { sprintf(str, "Ошибка выполнения SQL-запроса: %sn", err); sqlite3_free(err); return 2; } return 0; } // Загрузка таблицы из БД в буфер виджета int LoadDB() { char* SQL2 = "SELECT N, Фамилия, Оклад FROM Сотрудники;"; // Готовим SQL-запрос к БД if(sqlite3_prepare_v2(db, SQL2, -1, &stmt, NULL) != SQLITE_OK) { sprintf(str, "Ошибка подготовки SQL-запроса: %s\n", sqlite3_errmsg(db)); sqlite3_finalize(stmt); return 3; } // Цикл по SQL-запросу и запись в буфер таблицы while((rc = sqlite3_step(stmt)) == SQLITE_ROW) { gtk_list_store_append (buffer, &iter); gtk_list_store_set (buffer, &iter, C_NUM, sqlite3_column_int(stmt, C_NUM), C_NAME, sqlite3_column_text(stmt, C_NAME), C_OKLAD, (guint) sqlite3_column_int(stmt, C_OKLAD), -1); } if(rc != SQLITE_DONE) { sprintf(str, "Ошибка выполнения SQL-запроса: %s\n", sqlite3_errmsg(db)); sqlite3_finalize(stmt); return 4; } // 5. Освобождаем строку запроса sqlite3_finalize(stmt); return 0; } void ShowError () { GtkWidget *dialog = NULL; dialog = gtk_message_dialog_new (GTK_WINDOW (window1), GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, str); gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } int ShowMainWindow() { builder1 = gtk_builder_new (); if (gtk_builder_add_from_file (builder1, "main.glade", NULL)) { window1 = GTK_WIDGET(gtk_builder_get_object(builder1, "window1")); buffer = GTK_LIST_STORE(gtk_builder_get_object(builder1, "liststore1")); table = GTK_TREE_VIEW(gtk_builder_get_object(builder1, "treeview1")); gtk_window_set_default_size (GTK_WINDOW (window1), 500, 300); gtk_window_set_position(GTK_WINDOW(window1), GTK_WIN_POS_CENTER); gtk_builder_connect_signals (builder1, NULL); g_signal_connect(G_OBJECT(window1), "destroy", G_CALLBACK(gtk_main_quit), NULL); SaveDB(); if(LoadDB()) { ShowError(); } gtk_widget_show (window1); return TRUE; } else { return FALSE; } } int main (int argc, char *argv[]) { gtk_init (&argc, &argv); StartDB(); if (ShowMainWindow ()) { gtk_main (); EndDB(); return 0; } else { ShowGladeError(); EndDB(); return 1; } } |
В результате мы получаем вывод таблицы из базы данных на экран.
С помощью SQLite Studio можно подключиться к этой БД и увидеть, что все данные отражаются правильно.
Полностью проект можно скачать здесь
В Qt гораздо проще.
При создании таблицы лучше использовать форму:
CREATE TABLE IF NOT EXISTS Сотрудники
Вместо
DROP TABLE IF EXISTS Сотрудники.
Таким образом если база данных уже была создана (например, при повторном открытии программы), в первом случае данные сохранятся, а во втором (представленном на сайте), затрутся.
Можно и так. Зависит от цели.
Отсутствие поддержки кириллицы (русского языка) при формировании поисковых SQL-запросов, в ряде случаев сводят на нет все достоинства СУБД SQLite.
Но есть способы купировать эту проблему.
См., например, здесь:
https://roamer55.ru/main_programming/delphi/delphi_10_2_vcl/d10_funcs_db/d10_funcs_db_sqlite/d10_funcs_db_sqlite_upper/