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