Для роботи з контекстним меню 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?