使用动态内容刷新 wxGrid
这似乎并不像我希望的那么简单:
使用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 ();
}
特别是,这个网格充当一个列表,并且将通过相同或(可能)另一个进程异步添加和删除行 - 它是一个共享数据列表,可以由多个网络系统中的任何一个进行更新。网格/列表本身实际上是只读的;其他控件用于添加和删除项目,每一行都有一个也可以切换的布尔类型属性。
似乎新行没有添加到视图中,删除行将导致 wx 绘图代码中出现间歇性 SEGV。
由于动态/异步更新机制,我希望避免不断删除网格并将其重新添加到窗口,因为我确信这会导致各种闪烁和肮脏……所以,如果我绝对必须的话,我会尝试类似的暴力方法,但我强烈希望避免它。
不幸的是,尽管被标记为“稳定版本”,wxGrid 文档似乎主要由 Yet to be write
标签组成。
更新: 我开始怀疑这是容器布局问题。在绘制网格时,网格的底部(最后一行)实际上可以与 wxFrame 窗口的其部分周围的 wxStaticBox 框架以及框架的状态行的一部分重叠。添加和删除行似乎不会强制容器重新布局;我正在尝试调用 Layout
等。理想情况下,这应该是一个滚动区域,但 wxGrid应该仍然被“约束”在其包含的 Sizer 内。
该布局实际上由一个静态框组成,其中包含一个垂直框,其第一个元素是一个水平按钮框,然后是网格,如下所示:
--[ Static Box ]------------------------
| |
| [Button] [Button] [Button] |
| |
| ----------------------------------- |
| | | A | B | C | |
| |-----------------------------------| |
| | 1 | 1a | 1b | 1c | |
| ----------------------------------- |
| |
----------------------------------------
不幸的是,公司政策禁止我发布屏幕截图:-(
如果重要的话,这是(目前)Fedora 16 (x86_64) 上的 wxGTK-2.8.12,尽管我在使用 EPEL (Fedora) 软件包的 CentOS5/RHEL5 上看到了相同的行为。
This doesn't seem to be as simple as I'd hoped:
Using wxWidgets (2.8 stable series), I have a wxGrid (not subclassed) with a custom “data adapter” as a wxGridTableBase-derived class.
wxGrid* grid = new wxGrid (this, ID_TABLE);
grid->SetTable (new TableAdapter (foo, bar, baz));
grid->EnableEditing (false);
sizer->Add(grid, wxSizerFlags (1).Expand());
The “simple” thing that I can't find is a way to refresh the grid when the underlying data model changes. Simply calling wxWindow::Update (pGrid->Update()
) is apparently insufficient to actually get the grid to call the underlying wxGridTableBase implementation?
wxGrid* const grid = (wxGrid* const) FindWindow (ID_TABLE);
if (NULL != grid) {
grid->Update ();
grid->AutoSizeColumns ();
}
In particular, this grid is acting as a list, and will have rows added and removed from it asynchronously, by either the same or (potentially) another process — it's a shared data list that can be updated by any one of several networked systems. The grid/list itself is effectively read-only; other controls are used to add and remove items, and each row has one boolean-type attribute that can be toggled as well.
It seems that new rows aren't added to the view, and deleting rows will cause intermittent SEGV's in the wx drawing code.
Due to the dynamic/asynchronous updating mechanism, I'm hoping to avoid having to delete and re-add the grid to the window constantly, as I'm sure that will cause all sorts of flicker and nastiness… so, I'll fall back on trying something brute-force like that, if I absolutely must, but I'd strongly prefer to avoid it.
Unfortunately, despite being flagged as the “stable version,” the wxGrid documentation appears to mostly consist of Yet to be written
tags.
Updated:
I'm becoming suspicious that this is a layout-of-container problem. In drawing the grid, the bottom of the grid (last row) can actually overlap both the wxStaticBox frame around its section of the wxFrame window, as well as part of the status line of the frame. Adding and removing rows doesn't seem to force a re-layout of the container; I'm experimenting with trying to call Layout
and the like. Ideally, this should be a scrolling region, but the wxGrid should still be “constrained” within its containing Sizer.
The layout consists, effectively, of a static box, containing a vertical box, the first element of which is a horizontal box of buttons, then the grid, as so:
--[ Static Box ]------------------------
| |
| [Button] [Button] [Button] |
| |
| ----------------------------------- |
| | | A | B | C | |
| |-----------------------------------| |
| | 1 | 1a | 1b | 1c | |
| ----------------------------------- |
| |
----------------------------------------
Unfortunately, company policy prohibits me from posting screenshots :-(
If it matters, this is (presently) wxGTK-2.8.12 on Fedora 16 (x86_64), although I'm seeing identical behaviour on CentOS5/RHEL5 using the EPEL (Fedora) packages.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
经过多次实验,强制刷新的“正确”方法看起来是这样的:
在我的(5Hz)更新例程中,通过抓取
网格
并调用其->AppendRows(1)
方法,该方法依次调用wxTableBase
派生类的::AppendRows
成员。不幸的是,由于我是从异步、动态更新的记录中进行绘制,因此 wxGrid 缓存系统仍在与我“对抗”行属性(如果行发生更改,则其
GetAttr
值应该更改) ,它不会动态刷新,因为上面只测试应该存在的行数与实际存在的行数)。不过,这是一个相对较小的错误,我希望通过其他方式克服它。“关键”部分似乎是通过 ProcessTableMessage 将删除/追加行消息合成到 wxGridTable 系统......没有它,wxGrid 缓存似乎无法注意到表大小的变化。
顺便说一句,通过在
::GetValue(const int row, const int column)
方法中放置防护来检查有效值,可以缓解因丢失行而导致的崩溃:这些“×”值永远不会然而,在添加上面疯狂的消息注入逻辑之后,似乎显示了。
After much experimentation, it looks like the “correct” way to force a refresh goes something like this:
These are called during my (5Hz) update routine by grabbing
grid
and calling its->AppendRows(1)
method, which in turn calls thewxTableBase
-derived class's::AppendRows
member.Unfortunately, since I'm drawing from asynchronous, dynamically-updated records, the wxGrid caching system is still “fighting” me as far as row attributes (if a row changes, such that its
GetAttr
value should change, it doesn't refresh dynamically, since the above only tests the number of rows that there should be versus the number of rows that actually exist). However, this is a relatively minor bug, and I'm hoping to overcome it through other means.The “critical” part seems to be synthesizing the delete/append rows messages to the wxGridTable system through
ProcessTableMessage
… without which, the wxGrid cache seems to fail to notice the changes in the table size.Incidentally, the crashes due to losing rows were alleviated by putting guards in the
::GetValue(const int row, const int column)
method to check for valid values:These “×” values don't ever seem to display, after adding the crazy message-injection logic above, however.
不幸的是,调用
pGrid->Update()
似乎是不够的。对该函数的调用实际上会调用wxWindow::Update,它会递归地重新绘制窗口的无效区域及其所有子窗口,它可能无法正常工作。相反,您要调用的是
wxGrid::ForceRefresh()
,如文档 这里。文档说但有趣的是,如果您查看 wxWidgets 提供的示例项目中的网格示例,您会发现它们仅使用
wxGrid::Refresh
。我使用过 wxWidgets (C++) 和 wxPython,对于我的 wxGrid,我使用 ForceRefresh。可以在此处找到使用它的示例。虽然它是 wxPython,但我似乎没有找到使用 C++ 版本的在线示例,但是,在使用了这两个库后,我可以告诉您它们的使用方式相同。
Unfortunately it appears that a call to
pGrid->Update()
will not suffice. The call to that function will actually callwxWindow::Update
, which does repaint the invalided area of the window, along with all of its child recursively, it may not be working properly.Instead, what you want to call is
wxGrid::ForceRefresh()
as detailed in the documentation here. The documentation saysWhat is interesting though, is that if you look at the grid example on the samples project supplied with wxWidgets, they use just
wxGrid::Refresh
.I have used wxWidgets (C++) and wxPython, and for my wxGrid I use ForceRefresh. An example of it being used can be found here. While it is wxPython, I did not seem to find an online example of it using the C++ version, however, having used both libraries I can tell you that they are both used in the same manner.
导致网格立即重新绘制。使用它代替通常的 wxWindow::Refresh。
Causes immediate repainting of the grid. Use this instead of the usual wxWindow::Refresh.