Евгений Легоцкой31 серпня 2015 р. 10:39

Qt/C++ - Урок 013. QMenu - контекстное меню в QTableView

Для работы с контекстным меню в Qt используется класс QMenu . При совершении действий, которые должны вызвать меню, вызывается обработчик, который создаёт меню и привязывает обработчики к действиям в данном меню.

В данном уроке работа с контекстным меню будет показана на примере программного кода из урока по работе с QDataWidgetMapper . В данном уроке будет отредактировано два файла из предыдущего урока, но проект не заработает , если Вы не включите в него также файлы из предыдущего урока, которые не редактировались.

Программный код был написан в QtCreator 3.3.1 на основе Qt 5.4.1.

Структура проекта для QMenu

Структура проекта остается неизменной, как и в уроке, на котором основывается данный урок. Изменениям в программном коде будут подвергнуты лишь два файла:

  • mainwindow.h
  • mainwindow.cpp

mainwindow.h

Объявляем дополнительные СЛОТы в заголовочном файле. Это СЛОТы для вызова контекстного меню, и удаления записи. Также необходимо переписать сигнатуру СЛОТа для редактирования записи, поскольку будет использоваться иной способ определения выбранной записи.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSqlTableModel>

#include <database.h>
#include <dialogadddevice.h>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_addDeviceButton_clicked();
    void slotUpdateModels();
    /* К СЛОТу по редактировнию записи
     * добавляем СЛОТ по удалению записи.
     * Также добавляем СЛОТ для обработки вызова контекстного меню
     * */
    void slotEditRecord();
    void slotRemoveRecord();
    void slotCustomMenuRequested(QPoint pos);

private:
    Ui::MainWindow              *ui;
    DataBase                    *db;
    QSqlTableModel              *modelDevice;

private:
    void setupModel(const QString &tableName, const QStringList &headers);
    void createUI();
};

#endif // MAINWINDOW_H

mainwindow.cpp

В данном файле необходимо будет добавить включение контекстного меню для tableView. А также написать метод для обработки вызова контекстного меню и удаления записи из таблицы и соответственно из базы данных. Попутно переписываем и метод для редактирования записи.

В результате у Вас должно получиться приложение, которое по нажатию правой кнопкой мышки по записи в таблице вызывает контекстное меню с двумя пунктами: Редактировать и Удалить . По нажатию пункта Редактировать будет вызвано диалоговое окно, как в случае с действием double-click из предыдущего урока. А по нажатию пункта Удалить будет вызван MessageBox с вопросом о подтверждении удаления, и в случае утвердительного результата будет произведено удаление записи в таблице.

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    /* Программный код без изменения, как в уроке по QDataWidgetMapper */
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::setupModel(const QString &tableName, const QStringList &headers)
{
    /* Программный код без изменения, как в уроке по QDataWidgetMapper */
}

void MainWindow::createUI() 
{
    ui->deviceTableView->setModel(modelDevice);
    ui->deviceTableView->setColumnHidden(0, true);
    ui->deviceTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->deviceTableView->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->deviceTableView->resizeColumnsToContents();
    ui->deviceTableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->deviceTableView->horizontalHeader()->setStretchLastSection(true);

    // Устанавливаем Контекстное Меню
    ui->deviceTableView->setContextMenuPolicy(Qt::CustomContextMenu);

    connect(ui->deviceTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(slotEditRecord()));
    // Подключаем СЛОТ вызова контекстного меню
    connect(ui->deviceTableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotCustomMenuRequested(QPoint)));
}

/* Метод для активации диалога добавления записей
 * */
void MainWindow::on_addDeviceButton_clicked()
{
    /* Программный код без изменения, как в уроке по QDataWidgetMapper */
}

void MainWindow::slotCustomMenuRequested(QPoint pos)
{
    /* Создаем объект контекстного меню */
    QMenu * menu = new QMenu(this);
    /* Создаём действия для контекстного меню */
    QAction * editDevice = new QAction(trUtf8("Редактировать"), this);
    QAction * deleteDevice = new QAction(trUtf8("Удалить"), this);
    /* Подключаем СЛОТы обработчики для действий контекстного меню */
    connect(editDevice, SIGNAL(triggered()), this, SLOT(slotEditRecord()));     // Обработчик вызова диалога редактирования
    connect(deleteDevice, SIGNAL(triggered()), this, SLOT(slotRemoveRecord())); // Обработчик удаления записи
    /* Устанавливаем действия в меню */
    menu->addAction(editDevice);
    menu->addAction(deleteDevice);
    /* Вызываем контекстное меню */
    menu->popup(ui->deviceTableView->viewport()->mapToGlobal(pos));
}

/* Слот для удаления записи из таблицы
 * */
void MainWindow::slotRemoveRecord()
{
    /* Выясняем, какая из строк была выбрана
     * */
    int row = ui->deviceTableView->selectionModel()->currentIndex().row();
    /* Проверяем, что строка была действительно выбрана
     * */
    if(row >= 0){
        /* Задаём вопрос, стоит ли действительно удалять запись.
         * При положительном ответе удаляем запись
         * */
        if (QMessageBox::warning(this,
                                 trUtf8("Удаление записи"),
                                 trUtf8("Вы уверены, что хотите удалить эту запись?"),
                                 QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
        {
            /* При отрицательном ответе делаем откат действий
             * и закрываем диалог без удаления записи
             * */
            QSqlDatabase::database().rollback();
            return;
        } else {
            /* В противном случае производим удаление записи.
             * При успешном удалении обновляем таблицу.
             * */
            if(!modelDevice->removeRow(row)){
                QMessageBox::warning(this,trUtf8("Уведомление"),
                                     trUtf8("Не удалось удалить запись\n"
                                            "Возможно она используется другими таблицами\n"
                                            "Проверьте все зависимости и повторите попытку"));
            }
            modelDevice->select();
            ui->deviceTableView->setCurrentIndex(modelDevice->index(-1, -1));
        }
    }
}

/* Слот обновления модели представления данных
 * */
void MainWindow::slotUpdateModels()
{
    modelDevice->select();
    ui->deviceTableView->resizeColumnsToContents();
}

/* Метод для активации диалога добавления записей в режиме редактирования
 * с передачей индекса выбранной строки
 * */
void MainWindow::slotEditRecord()
{
    /* Также создаем диалог и подключаем его сигнал завершения работы
     * к слоту обновления вида модели представления данных, но передаём
     * в качестве параметров строку записи
     * */
    DialogAddDevice *addDeviceDialog = new DialogAddDevice(ui->deviceTableView->selectionModel()->currentIndex().row());
    connect(addDeviceDialog, SIGNAL(signalReady()), this, SLOT(slotUpdateModels()));

    /* Выполняем запуск диалогового окна
     * */
    addDeviceDialog->setWindowTitle(trUtf8("Редактировать Устройство"));
    addDeviceDialog->exec();
}

Итог

В результате Вы научились вызывать контекстное меню для объекта QTableView и в целом работать с классом QMenu. Также в качестве бонуса был параллельно освящен вопрос по удалению записей из таблицы и базы данных с помощью модели представления данных. Пример поведения QMenu показан в следующем видео:

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

Доброго дня.
У меня вопрос по поводу нового синтаксиса.
Никак не могу разобраться с подключением СЛОТ-а

connect(ui->deviceTableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotCustomMenuRequested(QPoint)));

... делаю

connect(ui->deviceTableView, QOverload<const QPoint &>::of(&QWidget::customContextMenuRequested),
        this, QOverload<const QPoint &>::of(&MainWindow::slotCustomMenuRequested));

... но с ошибкой.

Добрый день. Если у вас нет перегрузок сигналов или слотов, то QOverload Вам не нужен

connect(ui->deviceTableView, &QWidget::customContextMenuRequested, this, &MainWindow::slotCustomMenuRequested);

Ошибка при компиляции? Или QtCreator подсвечивает что-то красным без компиляции? И почему не привели текст ошибки? Экстрасены в отпуске.

AC

Спасибо за ответ. Да перегрузок сигналов нет.

Коментарі

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

Дозвольте порекомендувати вам чудовий хостинг, на якому розташований EVILEG.

Протягом багатьох років Timeweb доводив свою стабільність.

Для проектів на Django рекомендую VDS хостинг

Переглянути хостинг
J

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

  • Результат:93бали,
  • Рейтинг балів8
V
  • Vitreg
  • 26 жовтня 2020 р. 02:35

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

  • Результат:73бали,
  • Рейтинг балів1
V
  • Vitreg
  • 26 жовтня 2020 р. 02:23

C++ - Тест 005. Структуры и Классы

  • Результат:83бали,
  • Рейтинг балів4
Останні коментарі

Qt/C++ - Урок 074. Генерація псевдовипадкових чисел, використання STD бібліотеки random

А использование функции global() не решает ли эти проблемы? value = QRandomGenerator::global()->bounded(15, 43); Получаемая последовательность каждый раз новая.

Qt/C++ - Урок 074. Генерація псевдовипадкових чисел, використання STD бібліотеки random

А использование функции global() не решает ли эти проблемы? value = QRandomGenerator::global()->bounded(15, 43); Получаемая последовательность каждый раз новая.
S

QML - Урок 026. Intents с Qt для Android, часть 1

Есть ли возможность приведения java типа у QAndroidJniObject? Интересует конкретно class to
ВК

Qt / C ++ - Урок 015. QTableWidget або Як зробити таблицю з чекбоксами

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

Qt/C++ - Урок 006. QSqlQueryModel - Таблицы в Qt с помощью SQL-запросов

QSqlTableModel выполняет ряд стандартных операций для одной таблицы из базы данных. Поэтому там и реализован функционал по удалению и редактированию. QSqlQueryModel позволяет выполнить запр…
Тепер обговоріть на форумі
D

LibreOffice QT Widget

Я бы хотел интегрировать приложения из LibreOffice в свою программу. В идеале использовать их как виджеты Наткнулся на пакет libreoffice-qt5, который вроде как позволяет это делать htt…

Создание черновика как на авито и тд

А черновик в свою очередь нужен пока только для получения id, который нужен для мультизагружки изображений и привязки их к посту. как то так... я бы вообще решал это так: class P…
  • Nomad
  • 01 жовтня 2020 р. 05:22

MyForm(forms.Form): - непонятка

понятно спасибо
S

QWebView android

На android не запускается, иначе я бы не создавал этот пост. Собственно, вопрос я решил сам, там ещё понадобилось setDomStorageEnabled(true) вызвать.

не могу передать стринг с QLineEdit

QLineEdit *myLineEdit = new QLineEdit("line edit name", this); QString str = myLineEdit->text();
Про
Послуги
© EVILEG 2015-2020
Рекомендуємо хостинг TIMEWEB