Использование 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.
- #ifndef MAINWINDOW_H
- #define MAINWINDOW_H
- #include <QMainWindow>
- #include <QSqlQuery>
- /* My includes */
- #include <database.h>
- namespace Ui {
- class MainWindow;
- }
- class MainWindow : public QMainWindow
- {
- Q_OBJECT
- public:
- explicit MainWindow(QWidget *parent = 0);
- ~MainWindow();
- private:
- Ui::MainWindow *ui;
- DataBase *db;
- private:
- /* Метод для настройки интерфейса,
- * в данном методе будет выполняться заполнение QTableWidget
- * записями из таблицы
- * */
- void createUI(const QStringList &headers);
- };
- #endif // MAINWINDOW_H
mainwindow.cpp
В этом файле заключена цель всего урока, а именно настройка QTableWidget и заполнение его записями из базы данных.
- #include "mainwindow.h"
- #include "ui_mainwindow.h"
- MainWindow::MainWindow(QWidget *parent) :
- QMainWindow(parent),
- ui(new Ui::MainWindow)
- {
- ui->setupUi(this);
- this->setWindowTitle("QTableWidget Example");
- /* Первым делом необходимо создать объект для работы с базой данных
- * и инициализировать подключение к базе данных
- * */
- db = new DataBase();
- db->connectToDataBase();
- /* Наполним базу данных записями */
- for(int i = 1; i < 5; i++){
- /* Вставляем запись в таблицу, сразу устанавливаем состояние чекбокса.
- * Если устройство имеет нечётный номер, то статус чекбокса true,
- * иначе false
- * */
- db->inserIntoDeviceTable(QVariantList() << QString::number(i & 1)
- << "Device " + QString::number(i)
- << "192.168.0." + QString::number(i)
- << "AA:AA:AA:AA:AA:A" + QString::number(i));
- }
- /* Настраиваем внешний вид таблицы
- * с заданием названий колонок, а также
- * Заполняем таблицу записями из базы данных
- * */
- this->createUI(QStringList() << trUtf8("id")
- << trUtf8("Нечетность")
- << trUtf8("Имя компьютера")
- << trUtf8("IP адрес")
- << trUtf8("MAC адрес")
- );
- }
- MainWindow::~MainWindow()
- {
- delete ui;
- }
- /* Метод для настройки интерфейса,
- * в данном методе будет выполняться заполнение QTableWidget
- * записями из таблицы
- * */
- void MainWindow::createUI(const QStringList &headers)
- {
- ui->tableWidget->setColumnCount(5); // Указываем число колонок
- ui->tableWidget->setShowGrid(true); // Включаем сетку
- // Разрешаем выделение только одного элемента
- ui->tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
- // Разрешаем выделение построчно
- ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
- // Устанавливаем заголовки колонок
- ui->tableWidget->setHorizontalHeaderLabels(headers);
- // Растягиваем последнюю колонку на всё доступное пространство
- ui->tableWidget->horizontalHeader()->setStretchLastSection(true);
- // Скрываем колонку под номером 0
- ui->tableWidget->hideColumn(0);
- // Создаём запрос для для выборки записей из базы данных
- QSqlQuery query("SELECT "
- DEVICE ".id, "
- DEVICE "." DEVICE_CHECK_STATE ", "
- DEVICE "." DEVICE_HOSTNAME ", "
- DEVICE "." DEVICE_IP ", "
- DEVICE "." DEVICE_MAC
- " FROM " DEVICE);
- /* Выполняем заполнение QTableWidget записями с помощью цикла
- * */
- for(int i = 0; query.next(); i++){
- // Вставляем строку
- ui->tableWidget->insertRow(i);
- /* Устанавливаем в первую колонку id забирая его из результата SQL-запроса
- * Эта колонка будет скрыта
- * */
- ui->tableWidget->setItem(i,0, new QTableWidgetItem(query.value(0).toString()));
- // Создаём элемент, который будет выполнять роль чекбокса
- QTableWidgetItem *item = new QTableWidgetItem();
- item->data(Qt::CheckStateRole);
- /* Проверяем, на статус нечетности, если нечетное устройство, то
- * выставляем состояние чекбокса в Checked, иначе в Unchecked
- * */
- if(query.value(1).toInt() == 1){
- item->setCheckState(Qt::Checked);
- } else {
- item->setCheckState(Qt::Unchecked);
- }
- // Устанавливаем чекбокс во вторую колонку
- ui->tableWidget->setItem(i,1, item);
- // Далее забираем все данные из результата запроса и устанавливаем в остальные поля
- ui->tableWidget->setItem(i,2, new QTableWidgetItem(query.value(2).toString()));
- ui->tableWidget->setItem(i,3, new QTableWidgetItem(query.value(3).toString()));
- ui->tableWidget->setItem(i,4, new QTableWidgetItem(query.value(4).toString()));
- }
- // Ресайзим колонки по содержимому
- ui->tableWidget->resizeColumnsToContents();
- }
database.h
Данный файл отличается от того, что был взят из урока по QDataWidgetMapper тем, что была добавлена define директива для чекбокса, соответственно это повлекло изменение методов в файле database.cpp . А именно insertIntoDeviceTable и createDeviceTable .
- #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_CHECK_STATE "CheckState"
- #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() << "Не удалось восстановить базу данных";
- 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()
- {
- /* В данном случае используется формирование сырого SQL-запроса
- * с последующим его выполнением.
- * */
- QSqlQuery query;
- if(!query.exec( "CREATE TABLE " DEVICE " ("
- "id INTEGER PRIMARY KEY AUTOINCREMENT, "
- DEVICE_CHECK_STATE " INTEGER NOT NULL,"
- 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)
- {
- /* Запрос SQL формируется из QVariantList,
- * в который передаются данные для вставки в таблицу.
- * */
- QSqlQuery query;
- /* В начале SQL запрос формируется с ключами,
- * которые потом связываются методом bindValue
- * для подстановки данных из QVariantList
- * */
- query.prepare("INSERT INTO " DEVICE " ( " DEVICE_CHECK_STATE ", "
- DEVICE_HOSTNAME ", "
- DEVICE_IP ", "
- DEVICE_MAC " ) "
- "VALUES (:CheckState, :Hostname, :IP, :MAC )");
- query.bindValue(":CheckState", data[0].toInt());
- query.bindValue(":Hostname", data[1].toString());
- query.bindValue(":IP", data[2].toString());
- query.bindValue(":MAC", data[3].toString());
- // После чего выполняется запросом методом exec()
- if(!query.exec()){
- qDebug() << "error insert into " << DEVICE;
- qDebug() << query.lastError().text();
- return false;
- } else {
- return true;
- }
- return false;
- }
Итог
В результате при запуске программы будет создано приложение, в котором будет таблица с четырьмя записями, две из которых будут с отмеченными чекбоксами.
Спасибо за статью, не подскажите как можно использовать отмеченные чек боксом строчки. Мне их нужно скопировать их в другой QTableWidgetItem ?
Ответил на форуме .
В файле mainwindow.cpp строка 86:
item->data(Qt::CheckStateRole); // без слов
ну да, затесался неудалённый кусок ерунды, надо будет отредактировать на досуге статью.
Доброго времени суток.
Возник вопрос по статье.
Как организовать сортировку по столбцу с CheckBox.
Т.е. чтобы отмеченные строки отображались в верхней части TableWidget.
Дополнительный скрытый столбец делать или есть еще варианты?
Добрый день. Добавьте ORDER BY в заппрос SELECT по столбцу с чекбоксами.
Дело в том что чтолбец с чекбоксами никак не связан с базой. Он только для выбора объектов.
На ум приходит несколько вариантов:
Можно конечно, как-то написать костыли на изменение позиций строчек при наличии выделенного чекбокса, но это будут костыли. Лучше уж перейти на полноценную модель данных в данном случае и QTableView
Попробовал запустить код, описанный в данной статье, но получаю следующее:

Подскажите в чем может быть проблема ?
Вывод окна - пустой:
Пока добавляли у себя код, что-то пробовали проверяли, могло дойти до ситуации, когда у вас получилась создана таблица, с количеством колонок, не совпадающим с количеством колонок в финальной версии. В общем в INSERT запросе у вас больше или меньше аргументов, чем колонок в таблице DEVICE по факту.
Да, у меня тоже такая мысль возникла, но я просто скопипастил этот код из статьи, ничего в нем не меняя.
Кажется я понял в чем ошибка - я вручную создал таблицу Device в базе данных DataBase.db через DB Browser for SQLite в корне проекта с соответствующими типами данных и по какой-то причине insert не выполнялся, как только БД удалил, пример заработал.
Кто-нибудь знает, как сделать так, чтобы в QTableWidget состоящей из чекбоксов в строке таблицы можно было выбрать только один checkbox ?
ошибки куда-то пропали, но база данных не заполняется