程序在遍历目录树时在 JTextArea 中显示文件名,但我不知道如何通过按键停止它



有两个窗口:用于用户输入的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()获得输出 - 原始问题的最后几行。

它还没有完成,但它更令人满意。

相关内容

最新更新