Использование 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 ?
ошибки куда-то пропали, но база данных не заполняется