Russian Qt Forum

Qt => Model-View (MV) => Тема начата: silart от Ноябрь 07, 2010, 17:52



Название: QTableView + контекстное меню
Отправлено: silart от Ноябрь 07, 2010, 17:52
Добрый день!

У меня возник такой вопрос.
Есть модель QSqlTableModel. Есть представление QTableView. QTableView находится на своей форме, есть еще кнопки Insert, Delete, Submit, которые воздействуют на модель. Получается такой диалог, который может оперировать с данными модели: (добавлять строки, удалять строки, изменять строки) независимо от самой модели (то есть ему безразлично какие есть столбцы, сколько строк). Это универсальный диалог для заполнения справочников базы данных.
Так вот!
Каким образом, и возможно ли это, прикрутить к этому диалогу контекстное меню? Чтобы его пункты дублировали кнопки и чтобы действия меню применялись именно к выделенному элементу представления?

Кто-нибудь подобное делал?


Название: Re: QTableView + контекстное меню
Отправлено: Пантер от Ноябрь 07, 2010, 18:01
Легко. QAction тебе в помощь.


Название: Re: QTableView + контекстное меню
Отправлено: silart от Ноябрь 07, 2010, 18:19
Легко. QAction тебе в помощь.

Да это понятно все, что QAction. Меня интересует как прикрутить меню к QTableView. Как сделать так, чтобы меню органично сочиталось с QTableView? То есть если ничего не выделить, пункт Delete зачернялся бы.


Название: Re: QTableView + контекстное меню
Отправлено: asvil от Ноябрь 07, 2010, 20:09
Легко делать в Qt front-end'ы к базам данных будет тогда, когда компания Nokia прекратит заниматься ерундой, свернет проекты QtDeclarative, JavaScript и перейдет на cmake.
Автор будущей программы, рассмотрите пожалуйста другие инструменты для создания интерфейса. А если у Вас нет другого варианта, то ознакомтесь с содержанием, предоставляемым программой assistent. Просмотрите внимательно все сигналы и события, существуемые в Qt Framework. Затем попробуйте себя в фильтрации сообщений. Тем самым Вы получите доступ к действиям пользователя. В ответ на какое либо действие пользователя вы должны как-то изменить интерфейс (зачернить пункт delete в частности).
Давайте рассмотрим такое простое действие, как удаление строки в sql модели. Я бы рекомендовал предоставлять данное действие пользователю только после того, как последний выделит нужную(ые) строку(и) целиком. Отображать это действие будем в двух местах. Первое - QToolBar или QToolButton, второе - это всплывающее окно для вертикального заголовока QTableView.
Ниже я предоставляю псевдо-код с комментариями.
Код:
// инициализация
QAction *removeLinesAction = new QAction(QIcon(":/images/table/table_row_delete.png"), tr("Remove lines"), this);
removeLinesAction->setEnabled(false);
// Соединяем QAction со слотом удаления строк
connect(removeLinesAction, SIGNAL(triggered())
          , this, SLOT(removeLines()));
// соединяемся с сигналом, рассказывающем все о том, что выделил пользователь в QTableView
QTableView *itemView = new QTableView(this);
connect(itemView->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection))
  , this, SLOT(selectionChanged(QItemSelection, QItemSelection)));
QToolButton *removeLinesButton = new QToolButton(this);
removeLinesButton->setDefaultAction(removeLinesAction);

// Слот обрабатывающий изменение выделения
void selectionChanged(QItemSelection, QItemSelection)
{
  QAbstractItemModel* model;
  removeLinesAction->setEnabled(itemView->selectionModel()->selectedRows().count());
}

// Слот удаляющий строки
void removeLines()
{
  QAbstractItemModel* model;
  if ((model = itemView->model())) {
    QItemSelectionModel* selectionModel = itemView->selectionModel();
    QModelIndexList indexes = selectionModel->selectedRows();

    if (indexes.isEmpty())
      return;

    int messageBoxResult;
    messageBoxResult = QMessageBox::question(QApplication::activeWindow()
                       , tr("Removing data")
                       , tr("Data will be removed permanently. Continue?"),
                       QMessageBox::Yes, QMessageBox::No);

    if (messageBoxResult == QMessageBox::No)
      return;

    int count = 1;
    int startRow = indexes.at(0).row();
    QModelIndex rootIndex = indexes.at(0).parent();

    // delete only first continuous selection
    for (int i = 1; i < indexes.count(); ++i) {
      if (indexes.at(i - 1).row() != indexes.at(i).row() - 1)
        break;
      else
        ++count;
    }
    model->removeRows(startRow, count, rootIndex);

// Под вопросом нужен ли вызов данных функций здесь
    if (!model->submit())
      model->revert();

    return;
  }
}

// Переопределяем фильтр сообщений. В нашем случае фильтруем QTableView::verticalHeader()
bool eventFilter(QObject* object, QEvent* event)
{
  if (event->type() == QEvent::ContextMenu) {
    QContextMenuEvent* menuEvent = static_cast<QContextMenuEvent*>(event);
    QHeaderView* headerView = qobject_cast<QHeaderView*>(object);
    if (headerView) {
      if (headerView->orientation() == Qt::Vertical) {
        QList<QAction*> actions;
        actions.append(removeLinesAction);
        QMenu::exec(actions, menuEvent->globalPos(), 0, headerView);
        // отфильтровали
        return true;
      }
    }
  }
  return QObject::eventFilter(object, event);
}


Название: Re: QTableView + контекстное меню
Отправлено: xokc от Ноябрь 07, 2010, 20:21
Легко делать в Qt front-end'ы к базам данных будет тогда, когда компания Nokia прекратит заниматься ерундой, свернет проекты QtDeclarative, JavaScript и перейдет на cmake.
Боюсь, что сама Nokia этого делать не будет никогда - нафига оно ей? Ей надо мобильный бизнес спасать от Андроидов и  iOS. Остаётся только надеяться на достаточно серьезного стороннего девелопера (типа DevExpress), который прикрутит нормальный DB фронтенд к Qt.


Название: Re: QTableView + контекстное меню
Отправлено: silart от Ноябрь 10, 2010, 12:49
Филоненко Михаил, спасибо! Я воспользовался вашем советом.

QAction + eventFilter сделали свое дело. Контекстное меню хорошо интегрировалось в форму.


Название: Re: QTableView + контекстное меню
Отправлено: Sahab от Ноябрь 10, 2010, 13:57
Цитировать
Остаётся только надеяться на достаточно серьезного стороннего девелопера (типа DevExpress)
;D ;D ;D
и молиццо


Название: Re: QTableView + контекстное меню
Отправлено: xokc от Ноябрь 10, 2010, 21:47
Цитировать
Остаётся только надеяться на достаточно серьезного стороннего девелопера (типа DevExpress)
;D ;D ;D
и молиццо
А вот тут вряд ли поможет


Название: Re: QTableView + контекстное меню
Отправлено: Андрей от Январь 15, 2012, 20:05
Создал класс class MyTableView : public QTableView. В остальном сделал аналогично  приведённому примеру.  
Почему-то преобразования не происходит
QHeaderView* headerView = qobject_cast<QHeaderView*>(object);
Меняю на
QWidget *Widget = qobject_cast<QWidget*>(object);
всё работает нормально, но менюшка, соответственно, вылазит на каждом виджете MyTableView.


Название: Re: QTableView + контекстное меню
Отправлено: Андрей от Январь 20, 2012, 19:17
Нашёл ошибку.
Теперь другая проблема...
Запускается C:\Trainer\Ver1\Trainer-build-desktop-Qt_4_7_4_for_Desktop_-_MinGW_4_4__Qt_SDK_________\debug\Trainer.exe...
QObject::connect: Cannot connect (null)::selectionChanged(QItemSelection, QItemSelection) to MyTableView::selectionChanged(QItemSelection, QItemSelection)

Соответствующая строчка в MyTableView::MyTableView(QWidget *parent)

connect(this->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection))
      , this, SLOT(selectionChanged(QItemSelection, QItemSelection)));

Также хотелось бы, чтобы строка выделялась по нажатию на ней правой кнопки мыши, если она не была выделена.


Название: Re: QTableView + контекстное меню
Отправлено: BRE от Январь 20, 2012, 19:21
Соответствующая строчка в MyTableView::MyTableView(QWidget *parent)

connect(this->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection))
      , this, SLOT(selectionChanged(QItemSelection, QItemSelection)));
Этот коннект должен быть уже после установки модели (setModel), потому что селектион модель создается после этого или создавай и устанавливай ее сам.



Название: Re: QTableView + контекстное меню
Отправлено: Андрей от Январь 20, 2012, 19:38
Спасибо. Вставил в  MyTableView::setModel(...). Не ругается больше.