Для работы с контекстным меню в 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 показан в следующем видео:
Доброго дня.
У меня вопрос по поводу нового синтаксиса.
Никак не могу разобраться с подключением СЛОТ-а
... делаю
... но с ошибкой.
Добрый день. Если у вас нет перегрузок сигналов или слотов, то QOverload Вам не нужен
Ошибка при компиляции? Или QtCreator подсвечивает что-то красным без компиляции? И почему не привели текст ошибки? Экстрасены в отпуске.
Спасибо за ответ. Да перегрузок сигналов нет.
Добрый день,
в строке 49 файла mainwindow.cpp создаётся меню и оно будет создаваться каждый раз при его вызове. Т.е. каждый раз будет выделяться память под QMenu. Это же утечка памяти или Qt как то сам освобождает память при выходе их слота slotCustomMenuRequested?