有两个窗口:用于用户输入的GUI和用于找到的文件名列表的输出窗口。执行必须由用户通过按键停止,并且必须使两个窗口都保持打开状态,因为程序处理子目录,因此它可以运行很长时间,可能会逐步执行 100_000 个文件,要么产生大量输出,要么根本不生成,具体取决于用户的文件名模式如何匹配所选起始节点中遇到的文件。
这是我的问题:
如何查找按键(例如,ESC 或 CTRL-C)以允许用户终止? (单击红色 X 不是一个选项,因为这会关闭窗口;用户需要查看在终止之前找到的内容。这样做无论如何都不会关闭任何一个窗口,因为一旦树上漫步开始,所有按钮都会被禁用。
我尝试将 keyListeners 放在几个地方,但是一旦单击"开始"按钮,所有 swing 组件都会被禁用。
这似乎是一种很常见的情况,我很惊讶我找不到任何直接回答这个问题的教科书、线程或 Google 信息。所以恐怕这并不容易。这并不奇怪。我可能在这里找到了线索,但我无法编译它,并且其中包含的链接不会导致该代码片段。
单击"搜索"按钮时,搜索开始:
private void jbSearchActionPerformed(ActionEvent evt) {
SearchyGUI.doIt();
}
doIt()
方法通过 SimplefileVisitor
的扩展遍历目录树:
public class OverriddenFileVisitor extends SimpleFileVisitor<Path> {
...
}
public static void doIt(){
try {
visitor = new OverriddenFileVisitor();
info.setVisible(true);
Files.walkFileTree(SearchyGUI.p , visitor);
}
catch (Exception e) { }
}
}
输出通过 report()
方法写入jTextArea1
:
public static void report(String s){
Output.jTextArea1.append(s + "n");
}
这主要是在SimpleFileVisitor
visitFile()
方法中完成的:
public FileVisitResult visitFile(Path f, BasicFileAttributes a) throws IOException {
report(foundkt + "--" + f.getFileName().toString());
return FileVisitResult.CONTINUE;
}
这是主要类:
public class SearchyGUI {
static Output info;
static Path p ;
static FileVisitor visitor ;
static GUI gui
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
gui = new GUI();
gui.setVisible(true);
}
});
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
info = new Output();
}
});
}
问题是您占用了 GUI 线程,因此 GUI 线程无法处理源自用户的任何事件。
您需要创建一个new Thread
并在那里完成工作。然后,要显示该线程的输出,您可以使用 SwingUtilities.invokeLater
或类似的东西。
键绑定 API 可能是监视击键的最佳选择。
我还会在 UI 中添加一个 [取消] 按钮,它共享相同的操作......
public class CancelAction extends AbstractAction {
public CancelAction() {
putValue(NAME, "Cancel");
}
public void actionPerformed(ActionEvent evt) {
// Perform the cancel operation...
}
}
然后是代码中的其他位置...
CancelAction cancelAction = new CancelAction();
JButton cancelButton = new JButton(cancelAction);
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "Cancel");
am.put("Cancel", am);
现在,您将遇到的另一个问题是,您看起来像是在事件调度线程的上下文中运行长时间运行的任务。 这将阻止程序更新 UI 或允许用户与 UI 交互。
如果需要对UI进行更改(即显示文件处理的输出),则应尝试SwingWorker
。
主要原因是它允许您在另一个线程中执行长时间运行的任务,但提供了将更新重新同步回 EDT 的机制,在 EDT 中可以安全地对 UI 进行更改。
查看 Swing 中的并发性以获取更多详细信息。
无论你采取哪个方向,你都需要提供对正在执行任务的对象的引用,并提供某种"取消"标志,"任务"对象需要监视该标志。
我昨晚离开这个程序的方式并不令人满意,因为 Exit 导致用户无法看到到目前为止显示的输出(它可能很有用)。因此,我建立了窗口侦听器并使用 close 事件将布尔aborted
设置为 true,以防止进一步输出到窗口,但线程继续运行,如果在线程结束之前启动另一个搜索,这会导致间歇性问题。
这是我修复它的方法。
FileVisitor
接口有 4 种方法来实现遍历树 - 每个访问的文件有两个,每个目录两个。每个返回一个通常FileVisitResult.CONTINUE
FileVisitResult
。通过在文件访问者线程中将返回值更改为FileVisitResult.TERMINATE
,它会相应地终止!也就是说,我设置了一个标志,线程可以检查并采取适当的操作,这正是@MadProgrammer建议的。
public static FileVisitResult disposition = FileVisitResult.CONTINUE;
...
private static void report(String s){
if (! aborted)
try{
Output.jTextArea1.append(s + "n");
}
catch (Exception e){
aborted = true ;
disposition = FileVisitResult.TERMINATE;
}
}
...
@Override
public FileVisitResult visitFile(Path f, BasicFileAttributes a) throws IOException {
f1 = new File(f.getParent().toString() + "\" + f.getFileName().toString());
long filesize = f1.length();
report(f.getFileName().toString() + "t found in " + f.getParent().toString());
return disposition;
}
我是一个快乐的露营者!感谢您的想法和意见。
好吧,我让它停下来了。我想如果你在树林里徘徊足够长的时间,你会发现一个侏儒。我上周读了罗宾的暗示,有点放弃了。然后我读了一些越来越多的书。然后更多。但罗宾向我保证,侏儒确实存在于这些树林里!
我使用的代码是对我为MatLab/Java应用程序找到的一些代码的修改(为什么我什至要看它?最好的明显谷歌提示。
我按照 Robin 的建议将"文件访问者"(目录树步行者组件)作为线程启动:
public class OverriddenFileVisitor extends SimpleFileVisitor<Path> implements Runnable{
// ................................................................^^^^^^^^^^^^^^^^^^^
doIt()
年,我做了一些更改,将处理目录的行移动到现在可运行的类中,并在 doIt()
中将文件访问者作为自己的线程启动:
public static void doIt(){
try {
new OverriddenFileVisitor().startTh();
//^^^^^^^^^^
//(moved) Files.walkFileTree(SearchyGUI.p , visitor);
...
我将上一行中的新方法添加到 OverriddenFileVisitor 类中:(这是 MatLab/Java 代码的主要部分,对我来说很有意义,所以我使用并修改了它。
public void startTh() {
Thread t = new Thread(this);
t.start();
}
我为该类插入了重写的 run()
方法:
public void run() {
try {
Files.walkFileTree(SearchyGUI.p , this); // Used to be in doIt().
}
catch (IOException ex) { }
}
它运行并给出了正确的结果,并在我点击退出按钮时停止,该按钮在修改文件访问者以在其自己的线程中运行后"成为"启用,这就是@Robin Green所说的。我几乎觉得我知道我做了什么。
附言请注意,我已经能够通过invokeLater()
获得输出 - 原始问题的最后几行。
它还没有完成,但它更令人满意。