处理长时间运行的 EDT 任务(fi TreeModel 搜索)
触发器是最近重新检测到的 SwingX 问题:支持深度 - 即位于折叠节点下与仅可见节点相反,这是当前的行为 - 节点搜索。
“Nichts leichter als das”以及我当前对 SwingWorker 的所有了解:在后台线程中遍历 TreeModel 并在进程中更新 ui,如下面的粗略代码片段所示。 Fest 的 EDT 检查器很高兴,但它只检查重绘(这在 EDT 上很好地发生)
只是......严格来说,该后台线程必须是 EDT,因为它正在访问(通过读取)模型。那么,问题是:
- 如何正确地实现搜索线程?
- 或者我们是否可以忍受这种风险(当然,有大量记录)
特殊情况解决方案的一种可能性是使用第二个(克隆或“相同”制造的)模型进行搜索,然后在“真实”中找到相应的匹配项“ 模型。这与一般搜索支持的配合不太好,因为它无法了解任何特定模型的任何信息,即使需要也无法创建克隆。另外,它必须应用所有视图排序/过滤(将来)...
// a crude worker (match hard-coded and directly coupled to the ui)
public static class SearchWorker extends SwingWorker<Void, File> {
private Enumeration enumer;
private JXList list;
private JXTree tree;
public SearchWorker(Enumeration enumer, JXList list, JXTree tree) {
this.enumer = enumer;
this.list = list;
this.tree = tree;
}
@Override
protected Void doInBackground() throws Exception {
int count = 0;
while (enumer.hasMoreElements()) {
count++;
File file = (File) enumer.nextElement();
if (match(file)) {
publish(file);
}
if (count > 100){
count = 0;
Thread.sleep(50);
}
}
return null;
}
@Override
protected void process(List<File> chunks) {
for (File file : chunks) {
((DefaultListModel) list.getModel()).addElement(file);
TreePath path = createPathToRoot(file);
tree.addSelectionPath(path);
tree.scrollPathToVisible(path);
}
}
private TreePath createPathToRoot(File file) {
boolean result = false;
List<File> path = new LinkedList<File>();
while(!result && file != null) {
result = file.equals(tree.getModel().getRoot());
path.add(0, file);
file = file.getParentFile();
}
return new TreePath(path.toArray());
}
private boolean match(File file) {
return file.getName().startsWith("c");
}
}
// its usage in terms of SwingX test support
public void interactiveDeepSearch() {
final FileSystemModel files = new FileSystemModel(new File("."));
final JXTree tree = new JXTree(files);
tree.setCellRenderer(new DefaultTreeRenderer(IconValues.FILE_ICON, StringValues.FILE_NAME));
final JXList list = new JXList(new DefaultListModel());
list.setCellRenderer(new DefaultListRenderer(StringValues.FILE_NAME));
list.setVisibleRowCount(20);
JXFrame frame = wrapWithScrollingInFrame(tree, "search files");
frame.add(new JScrollPane(list), BorderLayout.SOUTH);
Action traverse = new AbstractAction("worker") {
@Override
public void actionPerformed(ActionEvent e) {
setEnabled(false);
Enumeration fileEnum = new PreorderModelEnumeration(files);
SwingWorker worker = new SearchWorker(fileEnum, list, tree);
PropertyChangeListener l = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
//T.imeOut("search end ");
setEnabled(true);
((SwingWorker) evt.getSource()).removePropertyChangeListener(this);
}
}
};
worker.addPropertyChangeListener(l);
// T.imeOn("starting search ... ");
worker.execute();
}
};
addAction(frame, traverse);
show(frame)
}
仅供参考:交叉发布到 OTN 的 Swing 论坛 和 SwingLabs论坛 - 将尝试在最后发布所有输入的摘要(如果有的话:-)
附录
在一天结束时,事实证明我问了错误的问题(或者错误上下文中的正确问题;-):“问题”是由假设的解决方案引起的,真正要解决的任务是支持分层搜索算法(目前 AbstractSearchable 严重偏向于线性搜索)。
一旦这个问题得到解决,下一个问题可能是框架可以在多大程度上支持具体的分层搜索。考虑到 TreeModel 的自定义实现多种多样,这很可能只适用于最简单的实现。
这里和其他论坛的讨论中出现了一些想法。在具体的上下文中,首先测量遍历是否缓慢:大多数内存模型的遍历速度都快如闪电,除了使用基本支持之外不需要执行任何操作。
仅当遍历是瓶颈时(如 SwingX 的 FileSystemModel 实现中的 fi),才需要额外的工作:
- 在真正不可变且不可修改的 TreeModel 中,我们可能会在 SwingWorker 的后台线程中进行只读访问,但
- 在惰性中违反了不可修改的前提条件加载/删除场景
- 可能有一个支持模型的自然自定义数据结构,它实际上与实际模型“分离”,允许同步到该支持模型(在这两种情况下)遍历和视图模型)
- 将实际搜索传递回数据库
- ,在给定的 TreeModel 之上使用包装器,保证访问 EDT
- “假”后台搜索上的底层模型:实际上是在 EDT 上足够小的块中执行此操作( fi 在计时器中),以便用户不会注意到任何延迟
无论慢速搜索的技术选项是什么,都需要解决相同的可用性问题:如何向最终用户呈现延迟?这是一个完全不同的故事,可能更依赖于上下文/需求:-)
Trigger is a recently re-detected SwingX issue: support deep - that is under collapsed nodes as opposed to visible nodes only, which is the current behaviour - node searching.
"Nichts leichter als das" with all my current exposure to SwingWorker: walk the TreeModel in the background thread and update the ui in process, like shown in a crude snippet below. Fest's EDT checker is happy enough, but then it only checks on repaint (which is nicely happening on the EDT here)
Only ... strictly speaking, that background thread must be the EDT as it is accessing (by reading) the model. So, the questions are:
- how to implement the search thread-correctly?
- or can we live with that risk (heavily documented, of course)
One possibility for a special case solution would be to have a second (cloned or otherwise "same"-made) model for searching and then find the corresponding matches in the "real" model. That doesn't play overly nicely with a general searching support, as that can't know anything about any particular model, that is can't create a clone even if it wanted. Plus it would have to apply all the view sorting/filtering (in future) ...
// a crude worker (match hard-coded and directly coupled to the ui)
public static class SearchWorker extends SwingWorker<Void, File> {
private Enumeration enumer;
private JXList list;
private JXTree tree;
public SearchWorker(Enumeration enumer, JXList list, JXTree tree) {
this.enumer = enumer;
this.list = list;
this.tree = tree;
}
@Override
protected Void doInBackground() throws Exception {
int count = 0;
while (enumer.hasMoreElements()) {
count++;
File file = (File) enumer.nextElement();
if (match(file)) {
publish(file);
}
if (count > 100){
count = 0;
Thread.sleep(50);
}
}
return null;
}
@Override
protected void process(List<File> chunks) {
for (File file : chunks) {
((DefaultListModel) list.getModel()).addElement(file);
TreePath path = createPathToRoot(file);
tree.addSelectionPath(path);
tree.scrollPathToVisible(path);
}
}
private TreePath createPathToRoot(File file) {
boolean result = false;
List<File> path = new LinkedList<File>();
while(!result && file != null) {
result = file.equals(tree.getModel().getRoot());
path.add(0, file);
file = file.getParentFile();
}
return new TreePath(path.toArray());
}
private boolean match(File file) {
return file.getName().startsWith("c");
}
}
// its usage in terms of SwingX test support
public void interactiveDeepSearch() {
final FileSystemModel files = new FileSystemModel(new File("."));
final JXTree tree = new JXTree(files);
tree.setCellRenderer(new DefaultTreeRenderer(IconValues.FILE_ICON, StringValues.FILE_NAME));
final JXList list = new JXList(new DefaultListModel());
list.setCellRenderer(new DefaultListRenderer(StringValues.FILE_NAME));
list.setVisibleRowCount(20);
JXFrame frame = wrapWithScrollingInFrame(tree, "search files");
frame.add(new JScrollPane(list), BorderLayout.SOUTH);
Action traverse = new AbstractAction("worker") {
@Override
public void actionPerformed(ActionEvent e) {
setEnabled(false);
Enumeration fileEnum = new PreorderModelEnumeration(files);
SwingWorker worker = new SearchWorker(fileEnum, list, tree);
PropertyChangeListener l = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
//T.imeOut("search end ");
setEnabled(true);
((SwingWorker) evt.getSource()).removePropertyChangeListener(this);
}
}
};
worker.addPropertyChangeListener(l);
// T.imeOn("starting search ... ");
worker.execute();
}
};
addAction(frame, traverse);
show(frame)
}
FYI: cross-posted to OTN's Swing forum and SwingLabs forum - will try to post a summary of all input at the end (if there is any :-)
Addendum
At the end of the day, it turned out that I asked the wrong question (or right question in a wrong context ;-): the "problem" arose by an assumed solution, the real task to solve is to support a hierarchical search algorithm (right now the AbstractSearchable is heavily skewed on linear search).
Once that will solved, the next question might be how much a framework can do to support concrete hierarchical searchables. Given the variety of custom implementations of TreeModels, that's most probably possible only for the most simple.
Some thoughts that came up in the discussions here and the other forums. In a concrete context, first measure if the traversal is slow: most in-memory models are lightning fast to traverse, nothing needs to be done except using the basic support.
Only if the traversal is the bottleneck (as f.i. in the FileSystemModel implementations of SwingX) additional work is needed:
- in a truly immutable and unmodifiable TreeModel we might get away with read-only access in a SwingWorker's background thread
- the unmodifiable precondition is violated in lazy loading/deleting scenarios
- there might be a natural custom data structure which backs the model, which is effectively kind-of "detached" from the actual model which allows synchronization to that backing model (in both traversal and view model)
- pass the actual search back to the database
- use an wrapper on top of a given TreeModel which guarantees to access the underlying model on the EDT
- "fake" background searching: actually do so in small-enough blocks on the EDT (f.i. in a Timer) so that the user doesn't notice any delay
Whatever the technical option to a slow search, there's the same usability problem to solve: how to present the delay to the end user? And that's an entirely different story, probably even more context/requirement dependent :-)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
SwingWorker
和TreeModel
都应该同步对通用的底层DataModel
的访问。使共享数据量(有效地)不可变可以最大限度地减少开销。由于这高度依赖于应用程序,因此我不确定该视图可以为搜索不可见节点提供多少支持。Both
SwingWorker
andTreeModel
should synchronize access to a common, underlyingDataModel
. Making the shared quanta of data (effectively) immutable can minimize the overhead. As this is highly application-dependent, I'm not sure how much support the view can offer for searching non-visible nodes.