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

#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 р. 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
  • 05 квітня 2019 р. 17:50

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

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

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

Ruslan Polupan
  • 07 квітня 2019 р. 01:48

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
Ua

Qt - Тест 001. Сигналы и слоты

  • Результат:84бали,
  • Рейтинг балів4
Ua

Qt - Тест 001. Сигналы и слоты

  • Результат:42бали,
  • Рейтинг балів-8
ОК

Qt - Тест 001. Сигналы и слоты

  • Результат:47бали,
  • Рейтинг балів-6
Останні коментарі
ИМ
Игорь Максимов22 листопада 2024 р. 21:51
Django - Підручник 017. Налаштуйте сторінку входу до Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi31 жовтня 2024 р. 23:37
Django - Урок 064. Як написати розширення для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 жовтня 2024 р. 17:19
Читалка файлів fb3 на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов05 жовтня 2024 р. 16:51
Django - Урок 064. Як написати розширення для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas505 липня 2024 р. 20:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Тепер обговоріть на форумі
AH
Abdul Hadi13 лютого 2025 р. 15:21
Are you Looking for best painter services in Qatar? Looking for top painter Services in Qatar? Get high-quality, affordable, and professional painting for homes & offices. Contact expert painters today!
d
dubaicushions13 лютого 2025 р. 15:17
Are Looking for custom swing cushions in Dubai for home decor? Looking for Custom Swing Cushions in Dubai? Get high-quality, weather-resistant, and stylish cushions for your outdoor swing. Order now for comfort & elegance!
d
dubaicustomizedsofa13 лютого 2025 р. 15:11
Are you Looking for a custom sofa in Dubai? Looking for a Custom Sofa in Dubai ? Get high-quality, stylish, and tailor-made sofas to match your space. Order now for comfort, luxury, and perfect design!
b
blinds1211 лютого 2025 р. 16:08
Why Bamboo Blinds Are the Perfect Choice for Your Home When it comes to enhancing the aesthetics and functionality of your living space, choosing the right window treatment is crucial. Bamboo blinds have emerged as a popular choice for homeowners wh…
i
imperial313011 лютого 2025 р. 15:40
How to Select the Right Carpet for Your Bedroom Aesthetic Choosing the perfect carpet for your bedroom involves more than just picking a color or pattern you like. Carpets can transform the ambiance of your space, adding warmth, comfort, and style. How…

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