Evgenii Legotckoi
Evgenii Legotckoi04 вересня 2015 р. 11: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.

#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;
}

Результат

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

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.

Вам це подобається? Поділіться в соціальних мережах!

Terabaytus
  • 13 червня 2017 р. 04:39

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

Evgenii Legotckoi
  • 15 червня 2017 р. 01:33

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

ЮВ
  • 12 січня 2019 р. 11:51

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

Evgenii Legotckoi
  • 14 січня 2019 р. 03:28

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

Ruslan Polupan
  • 05 квітня 2019 р. 07:50

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

Evgenii Legotckoi
  • 05 квітня 2019 р. 09:10
  • (відредаговано)

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

Ruslan Polupan
  • 06 квітня 2019 р. 15:48

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

Evgenii Legotckoi
  • 08 квітня 2019 р. 03:27

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

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

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

ВК
  • 09 вересня 2020 р. 06:35
  • (відредаговано)

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

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

Evgenii Legotckoi
  • 09 вересня 2020 р. 06:42

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

ВК
  • 09 вересня 2020 р. 06:45

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

ВК
  • 09 вересня 2020 р. 06:56
  • (відредаговано)

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

ВК
  • 29 вересня 2020 р. 08:08

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

СБ
  • 26 грудня 2020 р. 06:12
  • (відредаговано)

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

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
e
  • ehot
  • 31 березня 2024 р. 14:29

C++ - Тест 003. Условия и циклы

  • Результат:78бали,
  • Рейтинг балів2
B
  • Bogdannn
  • 27 березня 2024 р. 19:21

C++ - Тест 002. Константы

  • Результат:16бали,
  • Рейтинг балів-10
B
  • Bogdannn
  • 27 березня 2024 р. 19:15

C++ - Тест 001. Первая программа и типы данных

  • Результат:46бали,
  • Рейтинг балів-6
Останні коментарі
k
kmssr08 лютого 2024 р. 18:43
Qt Linux - Урок 001. Автозапуск програми Qt під Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко05 лютого 2024 р. 01:50
Qt WinAPI - Урок 007. Робота з ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 грудня 2023 р. 10:30
Boost - статичне зв&#39;язування в проекті CMake під Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 грудня 2023 р. 08:38
Boost - статичне зв&#39;язування в проекті CMake під Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik18 грудня 2023 р. 21:01
Qt/C++ - Урок 056. Підключення бібліотеки Boost в Qt для компіляторів MinGW і MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Тепер обговоріть на форумі
a
a_vlasov14 квітня 2024 р. 06:41
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел Дорофеев14 квітня 2024 р. 02:35
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrex04 квітня 2024 р. 04:47
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…
P
Pisych27 лютого 2023 р. 04:04
Как получить в массив значения из связанной модели? Спасибо, разобрался:))
AC
Alexandru Codreanu19 січня 2024 р. 11:57
QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…

Слідкуйте за нами в соціальних мережах