Обновление wxGrid с динамическим содержимым - PullRequest
5 голосов
/ 14 января 2012

Это не так просто, как я надеялся:

Используя wxWidgets (серия стабильных версий 2.8), у меня есть wxGrid (не в подклассах) с пользовательским «адаптером данных», как wxGridTableBase-произведенный класс.

wxGrid* grid = new wxGrid (this, ID_TABLE);
grid->SetTable (new TableAdapter (foo, bar, baz));
grid->EnableEditing (false);
sizer->Add(grid, wxSizerFlags (1).Expand());

«Простая» вещь, которую я не могу найти, - это способ обновления сетки при изменении базовой модели данных.Простого вызова wxWindow :: Update (pGrid->Update()) явно недостаточно для того, чтобы заставить сетку вызывать базовую реализацию wxGridTableBase?

wxGrid* const grid = (wxGrid* const) FindWindow (ID_TABLE);
if (NULL != grid) {
     grid->Update ();
     grid->AutoSizeColumns ();
}

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

Кажется, что новые строки не добавляются в представление, и удаление строк приведет кпрерывистые SEGV в коде чертежа wx.

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

К сожалению, несмотря на пометку «стабильной версии», документация по wxGrid в основном состоит из Yet to be written тегов.

Обновлено: У меня возникает подозрение, что это макетпроблема контейнера.При рисовании сетки нижняя часть сетки (последняя строка) может фактически перекрывать как кадр wxStaticBox вокруг своего раздела окна wxFrame, так и часть строки состояния кадра.Добавление и удаление строк, по-видимому, не приводит к изменению структуры контейнера;Я экспериментирую с попыткой звонить Layout и тому подобное.В идеале это должна быть область прокрутки, но wxGrid должен по-прежнему быть «ограниченным» внутри содержащего Sizer.

Макет состоит, по сути, из статического поля, содержащего вертикальное поле, первым элементом которого является горизонтальное поле кнопок, а затем сетка, как показано ниже:

    --[ Static Box ]------------------------
   |                                        |
   | [Button] [Button] [Button]             |
   |                                        |
   |  -----------------------------------   |
   | |      |   A      |   B   |    C    |  |
   | |-----------------------------------|  |
   | |    1 |   1a     |   1b  |    1c   |  |
   |  -----------------------------------   |
   |                                        |
    ----------------------------------------

К сожалению, политика компании запрещает мне публиковать скриншоты: - (

Если это имеет значение, это (в настоящее время) wxGTK-2.8.12 на Fedora 16 (x86_64), хотя я вижу идентичное поведение на CentOS5 / RHEL5используя пакеты EPEL (Fedora).

Ответы [ 3 ]

7 голосов
/ 30 января 2012

После долгих экспериментов выглядит, что «правильный» способ заставить обновление происходит примерно так:

bool
CDynamicWxGridTable::AppendRows(const size_t IGNORED _)
{
   wxGrid *grid = GetView();

   if (pGrid != NULL)
   {
      const int iNumRecords = GetNumberRows();
      const int iGridRows = grid->GetNumberRows();
      const int iNeedRows = iNumRecords - iGridRows;

      if (iNeedRows)
      {
         grid->BeginBatch();
         grid->ClearSelection();

         if (grid->IsCellEditControlEnabled())
         {
            grid->DisableCellEditControl();
         }

         {
            wxGridTableMessage pop(this,
                 wxGRIDTABLE_NOTIFY_ROWS_DELETED,
                 0, iGridRows);
            grid->ProcessTableMessage(pop);
         }
         {
            wxGridTableMessage push(this,
                 wxGRIDTABLE_NOTIFY_ROWS_APPENDED,
                 iNumRecords);
            grid->ProcessTableMessage(push);
         }
         grid->AutoSize();
         grid->ForceRefresh();
         grid->EndBatch();
      }
   }

   return true;
}

bool
CDynamicWxGridTable::DeleteRows(const size_t IGNORED pos,
      const size_t IGNORED rows)
{
   return AppendRows(0);
}

Они вызываются во время моей процедуры обновления (5 Гц) путем захвата grid и вызова его ->AppendRows(1) метод, который, в свою очередь, вызывает ::AppendRows член класса wxTableBase.

К сожалению, поскольку я рисую из асинхронных, динамически обновляемых записей,Система кеширования wxGrid по-прежнему «борется» со мной за атрибуты строки (если строка изменяется так, что ее значение GetAttr должно измениться, она не обновляется динамически, поскольку приведенное выше тестирует только количество строк, которые должны бытьпо сравнению с количеством строк, которые действительно существуют).Однако это относительно небольшая ошибка, и я надеюсь преодолеть ее с помощью других средств.

Кажется, что «критическая» часть синтезирует сообщения удаления / добавления строк в систему wxGridTable с помощью ProcessTableMessage… Без которого кеш wxGrid, похоже, не замечает изменений в размере таблицы.

Кстати, сбои из-за потери строк были уменьшены путем установки элементов защиты в методе ::GetValue(const int row, const int column) для проверки допустимых значений:

if (row < 0 || row > GetNumberRows()) { return L"×"; }
if (col < 0 || col > LAST_COLUMN) { return L"×"; }

Эти значения «×», кажется, никогда не отображаются, однако после добавления сумасшедшей логики внедрения сообщений выше.

1 голос
/ 22 января 2012

К сожалению, похоже, что вызова на pGrid->Update() будет недостаточно.Вызов этой функции на самом деле вызовет wxWindow::Update, который перерисовывает недействительную область окна вместе со всеми его дочерними рекурсивно, возможно, он не работает должным образом.

Вместо того, что вы хотите вызватьwxGrid::ForceRefresh(), как указано в документации здесь .В документации сказано:

Вызывает немедленное перекрашивание сетки.

Используйте это вместо обычного wxWindow::Refresh().

Что интересно, если вы посмотрите на пример сетки в примере проекта, поставляемого с wxWidgets, они используют толькоwxGrid::Refresh.

Я использовал wxWidgets (C ++) и wxPython, а для моей wxGrid я использую ForceRefresh.Пример его использования можно найти здесь .Несмотря на то, что это wxPython, я, похоже, не нашел в сети его примера с использованием версии C ++, однако, используя обе библиотеки, я могу вам сказать, что они обе используются одинаково.

1 голос
/ 14 января 2012

wxGrid :: ForceRefresh ()

Вызывает немедленное перекрашивание сетки.Используйте это вместо обычного wxWindow :: Refresh.

...