Evgenii Legotckoi
Evgenii Legotckoi31 серпня 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 хостинг.

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

AC
  • 04 березня 2020 р. 01:12

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

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));

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

Evgenii Legotckoi
  • 04 березня 2020 р. 03:06
  • (відредаговано)

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

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

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

AC
  • 04 березня 2020 р. 08:30

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

t
  • 24 липня 2021 р. 09:39

Добрый день,
в строке 49 файла mainwindow.cpp создаётся меню и оно будет создаваться каждый раз при его вызове. Т.е. каждый раз будет выделяться память под QMenu. Это же утечка памяти или Qt как то сам освобождает память при выходе их слота slotCustomMenuRequested?

Коментарі

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

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:30бали,
  • Рейтинг балів-10
ПЩ

C++ - Тест 004. Указатели, Массивы и Циклы

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

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

  • Результат:50бали,
  • Рейтинг балів-4
Останні коментарі
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" она решит проблему , лично мне помогло.
Тепер обговоріть на форумі
P
Pisych27 лютого 2023 р. 04:04
Как получить в массив значения из связанной модели? Спасибо, разобрался:))
AC
Alexandru Codreanu19 січня 2024 р. 11:57
QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…
BlinCT
BlinCT27 грудня 2023 р. 08:57
Растягивать Image на парент по высоте Ну и само собою дял включения scrollbar надо чтобы был Flickable. Так что выходит как то так Flickable{ id: root anchors.fill: parent clip: true property url linkFile p…
Дмитрий
Дмитрий10 січня 2024 р. 04:18
Qt Creator загружает всю оперативную память Проблема решена. Удалось разобраться с помощью утилиты strace. Запустил ее: strace ./qtcreator Начал выводиться весь лог работы креатора. В один момент он начал считывать фай…
Evgenii Legotckoi
Evgenii Legotckoi12 грудня 2023 р. 06:48
Побуквенное сравнение двух строк Добрый день. Там случайно не высылается этот сигнал textChanged ещё и при форматировани текста? Если решиать в лоб, то можно просто отключать сигнал/слотовое соединение внутри слота и …

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