На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела *nix / gcc / Eclipse / Qt / wxWidgets / GTK+
  • При создании темы ОБЯЗАТЕЛЬНО указывайте версию тулкита / библиотеки / компилятора.
  • Перед тем как задать вопрос, сформулируйте его правильно, чтобы вас могли понять.
  • Нарушение Правил может повлечь наказание со стороны модераторов.


Полезные ссылки:
user posted image Boost по-русски
user posted image Qt по-русски
Модераторы: Majestio
  
> таблица qt , таблица и выделенная строка
    Здравствуйте. Нужна таблица (что то типа потомка QTableView ) в которой текущая строка остаётся на месте при добавление данных. И при других изменениях модели, например удаление строк в начале.
    Не подскажите как сделать такое? Или взять готовое?
      Не знаю, я делал нечто похожее, чтоб отрисовывалось в QTableView, я делал наследование от QTableView c переопределением метода paintEvent + свои model и delegate.
        модель у меня тоже своя.
        А прокруткой можно настроить позицию?
          Не знаю, я пробовал, но не получилось.
            rodion, неправильное рассуждение. Наследовать QTableView не нужно - это вполне самодостаточный компонент. Отображением выделения занимается selection model (стандартная QItemSelectionModel). Я задал вопрос ИИ со своим предположением, и получил утвердительный ответ. Кстати получил и примеры кода:

            ExpandedWrap disabled
              // Пусть определения будут такие:
              // MyModel *model;          // наследник QAbstractTableModel, данные в QVector<QString>
              // QTableView *view;
               
              void insertRowsAbove(int position, const QStringList &newRows)
              {
                  if (newRows.isEmpty()) return;
               
                  QItemSelectionModel *sel = view->selectionModel();
                  const QModelIndex current = sel->currentIndex();
                  const int savedRow = current.isValid() ? current.row() : -1;
                  const int savedCol = current.isValid() ? current.column() : 0;
               
                  // --- изменение модели ---
                  model->beginInsertRows(QModelIndex(), position, position + newRows.size() - 1);
                  // здесь вставляете в QVector
                  // model->m_data.insert(position, ...);
                  model->endInsertRows();
                  // ------------------------
               
                  // Восстанавливаем позицию (номер строки)
                  if (savedRow >= 0) {
                      const int newRowCount = model->rowCount();
                      const int targetRow = qMin(savedRow, newRowCount - 1);  // clamp
                      if (targetRow >= 0) {
                          const QModelIndex idx = model->index(targetRow, savedCol);
                          sel->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
                          // или просто:
                          // view->setCurrentIndex(idx);
                      }
                  }
              }
               
              void removeRowsAbove(int position, int count)
              {
                  if (count <= 0) return;
               
                  QItemSelectionModel *sel = view->selectionModel();
                  const QModelIndex current = sel->currentIndex();
                  const int savedRow = current.isValid() ? current.row() : -1;
                  const int savedCol = current.isValid() ? current.column() : 0;
               
                  model->beginRemoveRows(QModelIndex(), position, position + count - 1);
                  // удаляете из QVector
                  model->endRemoveRows();
               
                  if (savedRow >= 0) {
                      const int newRowCount = model->rowCount();
                      // Если удалили слишком много сверху — позиция "поднимается"
                      const int targetRow = qMin(savedRow, newRowCount - 1);
                      if (targetRow >= 0) {
                          const QModelIndex idx = model->index(targetRow, savedCol);
                          sel->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
                      } else {
                          sel->clear();
                      }
                  }
              }

            Важные моменты

            • Добавление/удаление ниже выделенной строки на неё не влияет — это уже стандартное поведение, ничего делать не нужно.
            • Если у тебя множественное выделение (ExtendedSelection / MultiSelection), нужно сохранять не один currentIndex, а весь QItemSelection и сдвигать/восстанавливать диапазоны вручную (чуть сложнее, но принцип тот же).
            • Можно вынести логику в отдельный helper или в слот, подключённый к rowsInserted / rowsRemoved модели (если изменения приходят не только из твоего кода).
            • Кастомная модель здесь нужна только для того, чтобы правильно вызывать begin*/end* и хранить QVector. Сама логика «липкой» позиции — снаружи модели.

            Как альтернатива. Можно сделать тонкую обёртку над QItemSelectionModel (subclass), которая при получении rowsInserted/rowsRemoved не сдвигает выделение, а оставляет абсолютные номера строк. Но это уже более тяжёлое решение, и в 95 % случаев достаточно простого save/restore вокруг изменений данных.

            Как-то так. Код выше, естественно, я не проверял - это только для демонстрации принципа. Надо конечно тестировать, гарантия реализации требуемого не 100%.
              QItemSelectionModel не занимается прокруткой QTableView. Текущая строка может выйти из зоны видимости.
                Ok... QItemSelectionModel занимается только индексами выделения (и их сдвигом при rowsInserted/rowsRemoved).
                Прокруткой QTableView он не управляет. Поэтому после того, как строки выше выделенной вставились/удалились, текущая строка (даже если выделение корректно «поехало» вместе с данными) может спокойно уехать за пределы видимой области.
                Если цель - чтобы одна и та же строка данных оставалась выделенной и сохраняла свою визуальную позицию на экране (не прыгала вверх/вниз), то нужно делать две вещи:
                1) Позволить стандартному QItemSelectionModel сдвигать выделение (он это делает через persistent-индексы — это правильно)
                2) После изменения модели вручную поправить verticalScrollBar, чтобы выделенная строка вернулась на то же место в viewport
                См редакцию. кода:

                ExpandedWrap disabled
                  void insertRowsAbove(int position, const QStringList &newRows)
                  {
                      if (newRows.isEmpty())
                          return;
                   
                      QTableView *view = ...;
                      QAbstractItemModel *model = view->model();
                      QItemSelectionModel *sel = view->selectionModel();
                   
                      // 1. Запоминаем текущее состояние
                      const QModelIndex oldCurrent = sel->currentIndex();
                      const int oldRow = oldCurrent.isValid() ? oldCurrent.row() : -1;
                      const int oldCol = oldCurrent.isValid() ? oldCurrent.column() : 0;
                   
                      // Визуальная позиция выделенной строки относительно viewport
                      const QRect oldVisualRect = oldCurrent.isValid()
                                                      ? view->visualRect(oldCurrent)
                                                      : QRect();
                   
                      // 2. Вставляем строки (selection model сам сдвинет выделение)
                      model->beginInsertRows(QModelIndex(), position, position + newRows.size() - 1);
                      // ... вставка в QVector ...
                      model->endInsertRows();
                   
                      // 3. Восстанавливаем визуальную позицию
                      if (oldRow >= 0) {
                          // Индекс сдвинулся на количество вставленных строк
                          const QModelIndex newCurrent = model->index(oldRow + newRows.size(), oldCol);
                   
                          // На всякий случай явно ставим current (обычно уже стоит)
                          sel->setCurrentIndex(newCurrent, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
                   
                          // Считаем, насколько строка уехала, и компенсируем скроллом
                          const QRect newVisualRect = view->visualRect(newCurrent);
                          const int delta = newVisualRect.top() - oldVisualRect.top();
                   
                          if (delta != 0) {
                              QScrollBar *vbar = view->verticalScrollBar();
                              vbar->setValue(vbar->value() + delta);
                          }
                      }
                  }
                   
                  void removeRowsAbove(int position, int count)
                  {
                      if (count <= 0)
                          return;
                   
                      // ... те же запоминания oldCurrent / oldVisualRect ...
                   
                      model->beginRemoveRows(QModelIndex(), position, position + count - 1);
                      // ... удаление из QVector ...
                      model->endRemoveRows();
                   
                      if (oldRow >= 0) {
                          // После удаления индекс уменьшился
                          const int newRow = qMax(0, oldRow - count);
                          const QModelIndex newCurrent = model->index(newRow, oldCol);
                   
                          sel->setCurrentIndex(newCurrent, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
                   
                          const QRect newVisualRect = view->visualRect(newCurrent);
                          const int delta = newVisualRect.top() - oldVisualRect.top();
                   
                          QScrollBar *vbar = view->verticalScrollBar();
                          vbar->setValue(qMax(0, vbar->value() + delta));
                      }
                  }

                • visualRect() возвращает координаты относительно viewport. Поэтому разница top() — это именно то, на сколько пикселей нужно подвинуть скроллбар.
                • Если высота строк переменная — всё равно работает, потому что мы опираемся на реальный visualRect после изменения модели.
                • Если после удаления строк сверху выделенная строка «упёрлась» в начало — она естественно поднимется (это как раз то поведение, которое вы описывали: «только тогда поднимается выше»).
                • Добавление/удаление ниже выделенной строки на позицию не влияет — ничего делать не нужно.
                • Если используешь QAbstractItemView::ScrollPerPixel — код выше тоже корректен.

                Тести дальше.
                  Странно, почему то не работает. Почему то перекручивает.
                    Цитата rodion @
                    Странно, почему то не работает. Почему то перекручивает.

                    Ой, очень лениво пробовать. Я щя в отпуске. Если асилю - отпишу, но ничего не обещяю!
                      Отпуск это хорошо.
                      Может нужно крутить по индексам? Вариации на тему scrollTo?
                      0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                      0 пользователей:


                      Рейтинг@Mail.ru
                      [ Script execution time: 0.0985 ]   [ 16 queries used ]   [ Generated: 14.08.26, 00:08 GMT ]