Qt verwendet die Klasse QMenu , um mit dem Kontextmenü zu arbeiten. Beim Ausführen von Aktionen, die das Menü aufrufen sollen, wird ein Handler aufgerufen, der das Menü erstellt und Handler an die Aktionen in diesem Menü bindet.
In dieser Lektion wird die Arbeit mit dem Kontextmenü am Beispiel des Programmcodes aus der Lektion zum Arbeiten mit QDataWidgetMapper gezeigt. In dieser Lektion werden zwei Dateien aus der vorherigen Lektion bearbeitet, aber das Projekt funktioniert nicht , es sei denn, Sie beziehen auch die Dateien aus der vorherigen Lektion ein, die nicht bearbeitet wurden.
Der Programmcode wurde in QtCreator 3.3.1 basierend auf Qt 5.4.1 geschrieben.
Projektstruktur für QMenu
Die Struktur des Projekts bleibt dieselbe wie in der Lektion, die dieser Lektion zugrunde liegt. Im Programmcode werden nur zwei Dateien geändert:
- mainwindow.h
- mainwindow.cpp
mainwindow.h
Wir deklarieren zusätzliche SLOTS in der Header-Datei. Dies sind SLOTS zum Aufrufen des Kontextmenüs und zum Löschen eines Eintrags. Es ist auch erforderlich, die SLOT-Signatur neu zu schreiben, um den Datensatz zu bearbeiten, da eine andere Methode verwendet wird, um den ausgewählten Datensatz zu bestimmen.
#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
In dieser Datei muss das Kontextmenü für tableView hinzugefügt werden. Und auch eine Methode schreiben, um den Aufruf des Kontextmenüs zu behandeln und einen Datensatz aus der Tabelle und dementsprechend aus der Datenbank zu löschen . Nebenbei schreiben wir die Methode zum Bearbeiten des Beitrags neu.
Als Ergebnis sollten Sie eine Anwendung haben, die durch Rechtsklick auf einen Eintrag in der Tabelle ein Kontextmenü mit zwei Einträgen aufruft: Bearbeiten und Löschen . Durch Anklicken des Punkts Bearbeiten wird ein Dialogfenster aufgerufen, wie bei der Doppelklick-Aktion aus der vorherigen Lektion. Und durch Anklicken von Löschen wird die MessageBox mit einer Frage zur Bestätigung des Löschens aufgerufen, und im Falle eines positiven Ergebnisses wird der Datensatz in der Tabelle gelöscht.
#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(); }
Ergebnis
Als Ergebnis haben Sie gelernt, wie Sie das Kontextmenü für das QTableView -Objekt aufrufen und generell mit der QMenu-Klasse arbeiten. Als Bonus auch die Frage des Löschens von Datensätzen aus einer Tabelle und einer Datenbank mit das Datenansichtsmodell wurde parallel diskutiert. Ein Beispiel für das Verhalten von QMenu wird im folgenden Video gezeigt:
Доброго дня.
У меня вопрос по поводу нового синтаксиса.
Никак не могу разобраться с подключением СЛОТ-а
... делаю
... но с ошибкой.
Добрый день. Если у вас нет перегрузок сигналов или слотов, то QOverload Вам не нужен
Ошибка при компиляции? Или QtCreator подсвечивает что-то красным без компиляции? И почему не привели текст ошибки? Экстрасены в отпуске.
Спасибо за ответ. Да перегрузок сигналов нет.
Добрый день,
в строке 49 файла mainwindow.cpp создаётся меню и оно будет создаваться каждый раз при его вызове. Т.е. каждый раз будет выделяться память под QMenu. Это же утечка памяти или Qt как то сам освобождает память при выходе их слота slotCustomMenuRequested?