Обновление QListView - не запускает обновление

У меня следующая проблема:

когда я вызываю update() для QListView, его paintEvent() не запускается, если над виджетом не происходит какое-либо другое событие (перемещение мыши, получение фокуса....)

Я использую Qt 4.8.3, и если это определенно не ошибка в версии, я бы предпочел не обновлять (по моему опыту, обновления приносят больше проблем, чем преимуществ).

Вопрос: Как сделать так, чтобы QListViewQ...View) обновлялись в следующий раз, когда основной цикл получает управление?

Небольшая предыстория, что я решаю, если это поможет:

Имеется в виду однопоточное приложение.

Внизу находится некоторая независимая (не Qt) модель, которая является иерархической, и потребители запрашивают подэлементы. Элементы в нижней части иерархии могут быть изменены.

При модификации потребитель запрашивает W(ritable)Item. В этот момент части модели, на которые влияют отчеты об изменениях, "модифицируются" через подход наблюдателя. Таким образом, наблюдатели уведомляются в начале изменения (модель возвращает доступный для записи объект, не имеет контроля или понятия, когда изменение заканчивается).

Ожидается, что потребитель завершит модификацию до возврата из функции/метода, начавшей модификацию.

Ожидается, что модифицирующие методы/функции будут вызываться из основного потока, поэтому в следующий раз, когда основной поток возится с графическим интерфейсом, модель находится в согласованном состоянии, и потребители могут обновляться.

QModel выполняются для предоставления данных из приведенной ниже модели в формате, доступном для Qt.

Далее идут QWidget (списки/текстовые поля/метки), визуализирующие данные для пользователя, они изменены для поддержки метода Desync(), который помечает визуализированные данные как несинхронизированные, и переопределяют paintEvent, который проверяет состояние inSync. Для простых QWidget, таких как метки, при синхронизации вызывается обратный вызов, который просто заполняет данные. Для Q...View я предполагал заставить модели выдавать modelReset, поэтому список перезагружает количество строк и содержимое видимых.

Сверху находится класс, собирающий все это вместе в своем регионе, который подключается к наблюдателям, а по сообщениям об изменениях — Desync соответствующих виджетов.

Все методы, изменяющие что-либо, связаны через сигнал/слот Qt с кнопками/выпадающими списками/другими элементами графического интерфейса, поэтому я предполагаю, что все это выполняется в основном потоке.

Идея изменения:

  • Событие, вызванное графическим интерфейсом, основной поток начинает обработку метода изменения потребителя
  • потребитель получает необходимые предметы для сдачи
  • потребитель получает элементы, доступные для записи
  • Отчеты реальная модель изменены для наблюдателей
  • наблюдатели отмечают (Desync) соответствующие QWidgets как несинхронизированные
  • QWidget помечены как несинхронизированные и запланированы для обновления, но не пытаются получить доступ к чему-либо, так как мы работаем в основном потоке.
  • потребитель выполняет изменение, во время которого реальная модель может быть даже несовместимой
  • consumer возвращает управление тому, кто его вызвал
  • основной цикл выполняет обновления, которые переопределяются для синхронизации виджетов.

* Что я заметил: *

  • update() приводит к paintEvent для большинства виджетов, у которых вообще нет модели (метка/текстовое поле...)
  • update() не приводит к paintEvent для QListView
  • repaint() не помогает (была просто дикая попытка)
  • перемещение мыши над виджетом приводит к paintEvent, а QWidget синхронизируется
  • trying to visible(false); update(); visible(true); repaints immediately
    • that is wrong, as QWidget synchronizes before consumer performs the change
  • переключение окон (например, визуальная студия) или обратно приводит к вызову paintEvent

Упрощенные источники, откуда можно узнать о поведении:

мойСписок.h

  #ifndef __myList_h__
  #define __myList_h__

  #include <qlistview.h>

  class myList : public QListView
  {
     bool inSync;
     void sync();

     protected:
        virtual void paintEvent(QPaintEvent * event) override;

     public:
        myList(QWidget * parent);
        void Desync();
        virtual ~myList();
  };

  #endif

myList.cpp

  #include "myList.h"
  #include "myModel.h"

  void myList::sync()
  {
     if (inSync)
        return;

     inSync = true; //< set early, to prevent loops
     ((myModel*)model())->ResetModel();
  }

  void myList::paintEvent(QPaintEvent * event)
  {
     sync();
     QListView::paintEvent(event);
  }

  myList::myList(QWidget * parent) : QListView(parent), inSync(false)
  {}

  void myList::Desync()
  {
     inSync = false;
     update();
  }

  myList::~myList()
  {}

мояМодель.h

  #ifndef __myModel_h__
  #define __myModel_h__

  #include <QAbstractListModel>

  class myModel : public QAbstractListModel
  {
     Q_OBJECT;

     int & externalComplexData;

     public:
        myModel(int & externalComplexData);

        virtual int rowCount(QModelIndex const & parent = QModelIndex()) const override;
        virtual QVariant data(QModelIndex const & index, int role) const override;

        void ResetModel();

        virtual ~myModel();

  };

  #endif

myModel.cpp

  #include "myModel.h"

  myModel::myModel(int & externalComplexData) : externalComplexData(externalComplexData)
  {}

  int myModel::rowCount(QModelIndex const & parent) const
  {
     return 1;
  }

  QVariant myModel::data(QModelIndex const & index, int role) const
  {
     if (role != Qt::DisplayRole)
        return QVariant();

     return QString::number(externalComplexData);
  }

  void myModel::ResetModel()
  {
     reset();
  }

  myModel::~myModel()
  {}

tmp.h

  #ifndef __Tmp_H__
  #define __Tmp_H__

  #include <QtGui/QMainWindow>
  #include "ui_tmp.h"

  class tmp : public QMainWindow
  {
      Q_OBJECT

     public:
         tmp(QWidget *parent = 0, Qt::WFlags flags = 0);
         ~tmp();

     private:
         Ui::tmpClass ui;

     private slots:
         void clicked();
  };

  #endif

tmp.cpp

  #include "tmp.h"
  #include "myModel.h"

  int localComplexData = 0;

  tmp::tmp(QWidget *parent, Qt::WFlags flags)
      : QMainWindow(parent, flags)
  {
     ui.setupUi(this);

     ui.lst->setModel(new myModel(localComplexData));
     connect(ui.btn, SIGNAL(clicked()), this, SLOT(clicked()));
  }

  void tmp::clicked()
  {
     ui.lst->Desync();
     ++localComplexData;
  }

  tmp::~tmp()
  {}

Поведение. При нажатии на кнопку обновляется внешняя модель, но список не синхронизируется.

При перемещении мыши по списку он синхронизируется.

Ожидаемое поведение: Регистрация желания программиста на update() и результат paintEvent в следующий раз, когда основной цикл получит управление (или даже через несколько циклов).


person chmirko    schedule 23.05.2015    source источник
comment
Я забыл, в дизайнере я добавил QListView с именем lst и кнопку с именем btn   -  person chmirko    schedule 23.05.2015
comment
попробуй заставить repaint()   -  person user3528438    schedule 26.05.2015


Ответы (1)


Вы сделали это неправильно. Не трогайте QListView в этом нет необходимости. Просто исправьте модель данных (Qt), и все остальное будет работать из коробки.

Ваша модель myModel должна просто вызывать правильные методы при изменении данных. Эта модель должна учитывать источник реальных данных. Когда что-то случится с данными:

  • данные изменены - выдать сигнал dataChanged
  • данные добавляются или удаляются вызовом beginInsertRows и endInsertRows или другие соответствующие версии

Если вы делаете это правильно, больше ничего не нужно.

person Marek R    schedule 23.05.2015