Evgenii Legotckoi
4 сентября 2015 г. 21:30

Qt/C++ - Урок 015. QTableWidget или Как сделать таблицу с чекбоксами

Использование QTableWidget будет первым способом, который посоветуют Вам для создания таблиц с чекбоксами на Qt. Поэтому давайте рассмотрим и этот вариант работы с таблицами в Qt и конечно же применим чекбоксы.

Итак, чтобы урок был более приближен к реальности, захватим немного программного кода из урока по QDataWidgetMapper . А именно возьмём класс для работы с базой данных, чтобы уж сразу делать таблицу из базы данных. После чего сделаем форму главного окна приложения и выведем данные из таблицы с отображением чекбоксов. Естественно, при включении приложения таблица база данных будет создана и заполнена несколькими записями, которые мы и будем выводить в виджет.

Программный код был написан в QtCreator 3.3.1 на основе Qt 5.4.1.


Структура проекта для QTableWidget

Предлагаю ознакомиться со структурой проекта:

  • QTableWidgetExample.pro - профайл;
  • mainwindow.h - заголовочный файл основного окна приложения;
  • mainwindow.cpp - исходный код окна;
  • main.cpp - основной исходный файл, с которого стартует приложение;
  • mainwindow.ui - формочка основного окна приложения;
  • database.h - заголовочный файл вспомогательного класса, применяющегося для работы с информацией, которая помещена в базу данных;
  • database.cpp - исходный файл вспомогательного класса, применяющегося для работы с информацией, которая помещена в базу данных;

mainwindow.ui

Всё, что Вам нужно сделать с этим файлом, это кинуть в форму главного окна в дизайнере QTableWidget.

mainwindow.h

В данном файле объявляется объект базы данных, с которым мы будем работать, а также метод для заполнения данными QTableWidget.

  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3.  
  4. #include <QMainWindow>
  5. #include <QSqlQuery>
  6.  
  7. /* My includes */
  8. #include <database.h>
  9.  
  10. namespace Ui {
  11. class MainWindow;
  12. }
  13.  
  14. class MainWindow : public QMainWindow
  15. {
  16. Q_OBJECT
  17.  
  18. public:
  19. explicit MainWindow(QWidget *parent = 0);
  20. ~MainWindow();
  21.  
  22. private:
  23. Ui::MainWindow *ui;
  24. DataBase *db;
  25.  
  26. private:
  27. /* Метод для настройки интерфейса,
  28. * в данном методе будет выполняться заполнение QTableWidget
  29. * записями из таблицы
  30. * */
  31. void createUI(const QStringList &headers);
  32. };
  33.  
  34. #endif // MAINWINDOW_H

mainwindow.cpp

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

  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3.  
  4. MainWindow::MainWindow(QWidget *parent) :
  5. QMainWindow(parent),
  6. ui(new Ui::MainWindow)
  7. {
  8. ui->setupUi(this);
  9.  
  10. this->setWindowTitle("QTableWidget Example");
  11. /* Первым делом необходимо создать объект для работы с базой данных
  12. * и инициализировать подключение к базе данных
  13. * */
  14. db = new DataBase();
  15. db->connectToDataBase();
  16.  
  17. /* Наполним базу данных записями */
  18. for(int i = 1; i < 5; i++){
  19. /* Вставляем запись в таблицу, сразу устанавливаем состояние чекбокса.
  20. * Если устройство имеет нечётный номер, то статус чекбокса true,
  21. * иначе false
  22. * */
  23. db->inserIntoDeviceTable(QVariantList() << QString::number(i & 1)
  24. << "Device " + QString::number(i)
  25. << "192.168.0." + QString::number(i)
  26. << "AA:AA:AA:AA:AA:A" + QString::number(i));
  27. }
  28.  
  29. /* Настраиваем внешний вид таблицы
  30. * с заданием названий колонок, а также
  31. * Заполняем таблицу записями из базы данных
  32. * */
  33. this->createUI(QStringList() << trUtf8("id")
  34. << trUtf8("Нечетность")
  35. << trUtf8("Имя компьютера")
  36. << trUtf8("IP адрес")
  37. << trUtf8("MAC адрес")
  38. );
  39. }
  40.  
  41. MainWindow::~MainWindow()
  42. {
  43. delete ui;
  44. }
  45.  
  46. /* Метод для настройки интерфейса,
  47. * в данном методе будет выполняться заполнение QTableWidget
  48. * записями из таблицы
  49. * */
  50. void MainWindow::createUI(const QStringList &headers)
  51. {
  52. ui->tableWidget->setColumnCount(5); // Указываем число колонок
  53. ui->tableWidget->setShowGrid(true); // Включаем сетку
  54. // Разрешаем выделение только одного элемента
  55. ui->tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
  56. // Разрешаем выделение построчно
  57. ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
  58. // Устанавливаем заголовки колонок
  59. ui->tableWidget->setHorizontalHeaderLabels(headers);
  60. // Растягиваем последнюю колонку на всё доступное пространство
  61. ui->tableWidget->horizontalHeader()->setStretchLastSection(true);
  62. // Скрываем колонку под номером 0
  63. ui->tableWidget->hideColumn(0);
  64.  
  65. // Создаём запрос для для выборки записей из базы данных
  66. QSqlQuery query("SELECT "
  67. DEVICE ".id, "
  68. DEVICE "." DEVICE_CHECK_STATE ", "
  69. DEVICE "." DEVICE_HOSTNAME ", "
  70. DEVICE "." DEVICE_IP ", "
  71. DEVICE "." DEVICE_MAC
  72. " FROM " DEVICE);
  73.  
  74. /* Выполняем заполнение QTableWidget записями с помощью цикла
  75. * */
  76. for(int i = 0; query.next(); i++){
  77. // Вставляем строку
  78. ui->tableWidget->insertRow(i);
  79. /* Устанавливаем в первую колонку id забирая его из результата SQL-запроса
  80. * Эта колонка будет скрыта
  81. * */
  82. ui->tableWidget->setItem(i,0, new QTableWidgetItem(query.value(0).toString()));
  83.  
  84. // Создаём элемент, который будет выполнять роль чекбокса
  85. QTableWidgetItem *item = new QTableWidgetItem();
  86. item->data(Qt::CheckStateRole);
  87. /* Проверяем, на статус нечетности, если нечетное устройство, то
  88. * выставляем состояние чекбокса в Checked, иначе в Unchecked
  89. * */
  90. if(query.value(1).toInt() == 1){
  91. item->setCheckState(Qt::Checked);
  92. } else {
  93. item->setCheckState(Qt::Unchecked);
  94. }
  95. // Устанавливаем чекбокс во вторую колонку
  96. ui->tableWidget->setItem(i,1, item);
  97. // Далее забираем все данные из результата запроса и устанавливаем в остальные поля
  98. ui->tableWidget->setItem(i,2, new QTableWidgetItem(query.value(2).toString()));
  99. ui->tableWidget->setItem(i,3, new QTableWidgetItem(query.value(3).toString()));
  100. ui->tableWidget->setItem(i,4, new QTableWidgetItem(query.value(4).toString()));
  101. }
  102.  
  103. // Ресайзим колонки по содержимому
  104. ui->tableWidget->resizeColumnsToContents();
  105. }

database.h

Данный файл отличается от того, что был взят из урока по QDataWidgetMapper тем, что была добавлена define директива для чекбокса, соответственно это повлекло изменение методов в файле database.cpp . А именно insertIntoDeviceTable и createDeviceTable .

  1. #ifndef DATABASE_H
  2. #define DATABASE_H
  3.  
  4. #include <QObject>
  5. #include <QSql>
  6. #include <QSqlQuery>
  7. #include <QSqlError>
  8. #include <QSqlDatabase>
  9. #include <QFile>
  10. #include <QDate>
  11. #include <QDebug>
  12.  
  13. /* Директивы имен таблицы, полей таблицы и базы данных */
  14. #define DATABASE_HOSTNAME "ExampleDataBase"
  15. #define DATABASE_NAME "DataBase.db"
  16.  
  17. #define DEVICE "DeviceTable"
  18. #define DEVICE_CHECK_STATE "CheckState"
  19. #define DEVICE_HOSTNAME "Hostname"
  20. #define DEVICE_IP "IP"
  21. #define DEVICE_MAC "MAC"
  22.  
  23. class DataBase : public QObject
  24. {
  25. Q_OBJECT
  26. public:
  27. explicit DataBase(QObject *parent = 0);
  28. ~DataBase();
  29. /* Методы для непосредственной работы с классом
  30. * Подключение к базе данных и вставка записей в таблицу
  31. * */
  32. void connectToDataBase();
  33. bool inserIntoDeviceTable(const QVariantList &data);
  34.  
  35. private:
  36. // Сам объект базы данных, с которым будет производиться работа
  37. QSqlDatabase db;
  38.  
  39. private:
  40. /* Внутренние методы для работы с базой данных
  41. * */
  42. bool openDataBase();
  43. bool restoreDataBase();
  44. void closeDataBase();
  45. bool createDeviceTable();
  46. };
  47.  
  48. #endif // DATABASE_H

database.cpp

  1. #include "database.h"
  2.  
  3. DataBase::DataBase(QObject *parent) : QObject(parent)
  4. {
  5.  
  6. }
  7.  
  8. DataBase::~DataBase()
  9. {
  10.  
  11. }
  12.  
  13. /* Методы для подключения к базе данных
  14. * */
  15. void DataBase::connectToDataBase()
  16. {
  17. /* Перед подключением к базе данных производим проверку на её существование.
  18. * В зависимости от результата производим открытие базы данных или её восстановление
  19. * */
  20. if(!QFile("C:/example/" DATABASE_NAME).exists()){
  21. this->restoreDataBase();
  22. } else {
  23. this->openDataBase();
  24. }
  25. }
  26.  
  27. /* Методы восстановления базы данных
  28. * */
  29. bool DataBase::restoreDataBase()
  30. {
  31. if(this->openDataBase()){
  32. if(!this->createDeviceTable()){
  33. return false;
  34. } else {
  35. return true;
  36. }
  37. } else {
  38. qDebug() << "Не удалось восстановить базу данных";
  39. return false;
  40. }
  41. return false;
  42. }
  43.  
  44. /* Метод для открытия базы данных
  45. * */
  46. bool DataBase::openDataBase()
  47. {
  48. /* База данных открывается по заданному пути
  49. * и имени базы данных, если она существует
  50. * */
  51. db = QSqlDatabase::addDatabase("QSQLITE");
  52. db.setHostName(DATABASE_HOSTNAME);
  53. db.setDatabaseName("C:/example/" DATABASE_NAME);
  54. if(db.open()){
  55. return true;
  56. } else {
  57. return false;
  58. }
  59. }
  60.  
  61. /* Методы закрытия базы данных
  62. * */
  63. void DataBase::closeDataBase()
  64. {
  65. db.close();
  66. }
  67.  
  68. /* Метод для создания таблицы устройств в базе данных
  69. * */
  70. bool DataBase::createDeviceTable()
  71. {
  72. /* В данном случае используется формирование сырого SQL-запроса
  73. * с последующим его выполнением.
  74. * */
  75. QSqlQuery query;
  76. if(!query.exec( "CREATE TABLE " DEVICE " ("
  77. "id INTEGER PRIMARY KEY AUTOINCREMENT, "
  78. DEVICE_CHECK_STATE " INTEGER NOT NULL,"
  79. DEVICE_HOSTNAME " VARCHAR(255) NOT NULL,"
  80. DEVICE_IP " VARCHAR(16) NOT NULL,"
  81. DEVICE_MAC " VARCHAR(18) NOT NULL"
  82. " )"
  83. )){
  84. qDebug() << "DataBase: error of create " << DEVICE;
  85. qDebug() << query.lastError().text();
  86. return false;
  87. } else {
  88. return true;
  89. }
  90. return false;
  91. }
  92.  
  93. /* Метод для вставки записи в таблицу устройств
  94. * */
  95. bool DataBase::inserIntoDeviceTable(const QVariantList &data)
  96. {
  97. /* Запрос SQL формируется из QVariantList,
  98. * в который передаются данные для вставки в таблицу.
  99. * */
  100. QSqlQuery query;
  101. /* В начале SQL запрос формируется с ключами,
  102. * которые потом связываются методом bindValue
  103. * для подстановки данных из QVariantList
  104. * */
  105. query.prepare("INSERT INTO " DEVICE " ( " DEVICE_CHECK_STATE ", "
  106. DEVICE_HOSTNAME ", "
  107. DEVICE_IP ", "
  108. DEVICE_MAC " ) "
  109. "VALUES (:CheckState, :Hostname, :IP, :MAC )");
  110. query.bindValue(":CheckState", data[0].toInt());
  111. query.bindValue(":Hostname", data[1].toString());
  112. query.bindValue(":IP", data[2].toString());
  113. query.bindValue(":MAC", data[3].toString());
  114. // После чего выполняется запросом методом exec()
  115. if(!query.exec()){
  116. qDebug() << "error insert into " << DEVICE;
  117. qDebug() << query.lastError().text();
  118. return false;
  119. } else {
  120. return true;
  121. }
  122. return false;
  123. }

Итог

В результате при запуске программы будет создано приложение, в котором будет таблица с четырьмя записями, две из которых будут с отмеченными чекбоксами.

Вам это нравится? Поделитесь в социальных сетях!

Terabaytus
  • 13 июня 2017 г. 14:39

Спасибо за статью, не подскажите  как можно использовать отмеченные чек боксом строчки. Мне их нужно скопировать их в другой QTableWidgetItem ?

Evgenii Legotckoi
  • 15 июня 2017 г. 11:33

Ответил на форуме .

ЮВ
  • 12 января 2019 г. 22:51

В файле mainwindow.cpp строка 86:
item->data(Qt::CheckStateRole); // без слов

Evgenii Legotckoi
  • 14 января 2019 г. 14:28

ну да, затесался неудалённый кусок ерунды, надо будет отредактировать на досуге статью.

Ruslan Polupan
  • 5 апреля 2019 г. 17:50

Доброго времени суток.
Возник вопрос по статье.
Как организовать сортировку по столбцу с CheckBox.
Т.е. чтобы отмеченные строки отображались в верхней части TableWidget.
Дополнительный скрытый столбец делать или есть еще варианты?

Evgenii Legotckoi
  • 5 апреля 2019 г. 19:10
  • (ред.)

Добрый день. Добавьте ORDER BY в заппрос SELECT по столбцу с чекбоксами.

Ruslan Polupan
  • 7 апреля 2019 г. 1:48

Дело в том что чтолбец с чекбоксами никак не связан с базой. Он только для выбора объектов.

Evgenii Legotckoi
  • 8 апреля 2019 г. 13:27

На ум приходит несколько вариантов:

  • добавлять все взятые из БД записи в вектор структур данных, в которых будет дополнительное поле под чекбокс и выполнять сортировку этого вектора по полю чекбокса.
  • Всё тоже самое, только уже переписать на QTableView и сделать полноценную модель данных.

Можно конечно, как-то написать костыли на изменение позиций строчек при наличии выделенного чекбокса, но это будут костыли. Лучше уж перейти на полноценную модель данных в данном случае и QTableView

ВК
  • 9 сентября 2020 г. 16:35
  • (ред.)

Попробовал запустить код, описанный в данной статье, но получаю следующее:

Подскажите в чем может быть проблема ?
Вывод окна - пустой:

Evgenii Legotckoi
  • 9 сентября 2020 г. 16:42

Пока добавляли у себя код, что-то пробовали проверяли, могло дойти до ситуации, когда у вас получилась создана таблица, с количеством колонок, не совпадающим с количеством колонок в финальной версии. В общем в INSERT запросе у вас больше или меньше аргументов, чем колонок в таблице DEVICE по факту.

ВК
  • 9 сентября 2020 г. 16:45

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

ВК
  • 9 сентября 2020 г. 16:56
  • (ред.)

Кажется я понял в чем ошибка - я вручную создал таблицу Device в базе данных DataBase.db через DB Browser for SQLite в корне проекта с соответствующими типами данных и по какой-то причине insert не выполнялся, как только БД удалил, пример заработал.

ВК
  • 29 сентября 2020 г. 18:08

Кто-нибудь знает, как сделать так, чтобы в QTableWidget состоящей из чекбоксов в строке таблицы можно было выбрать только один checkbox ?

СБ
  • 26 декабря 2020 г. 17:12
  • (ред.)

ошибки куда-то пропали, но база данных не заполняется

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь