0

有两个窗口:用户输入的 GUI 和找到的文件名列表的输出窗口。执行必须是用户可以通过按键停止的,并且必须让两个窗口都打开,因为程序处理子目录,因此它可以运行很长时间,可能会单步执行 100_000 个文件,产生大量输出或根本不产生输出,具体取决于用户的文件名模式匹配所选起始节点中遇到的文件。

这是我的问题:

如何查找允许用户终止的按键(例如,ESC 或 CTRL-C)?(单击红色 X 不是一个选项,因为它会关闭窗口;用户需要在终止之前查看找到的内容。这样做不会关闭任何一个窗口,因为一旦树遍历开始,所有按钮都将被禁用。)

我尝试将 keyListeners 放在几个地方,但是一旦单击“开始”按钮,所有的摆动组件都被禁用。

这似乎是一种常见的情况,我很惊讶我找不到任何直接回答问题的教科书、线程或 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) {    }
  }   
}

jTextArea1输出通过以下方法写入report()

  public static void report(String s){
    Output.jTextArea1.append(s + "\n");
  }

这主要通过以下visitFile()方法完成SimpleFileVisitor

 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();
      }
    });
  }
4

4 回答 4

2

问题是您占用了 GUI 线程,因此 GUI 线程无法处理源自用户的任何事件。

您需要创建一个new Thread并在那里完成工作。然后,要显示该线程的输出,您可以使用SwingUtilities.invokeLater或类似的东西。

于 2013-11-08T22:49:41.273 回答
2

Key Bindings API 可能是监控击键的最佳选择。

我还会在 UI 中添加一个 [Cancel] 按钮,它共享相同的操作...

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);

现在您将遇到的另一个问题是,您看起来像是在 Event Dispatching Thread 的上下文中运行一个长时间运行的任务。这将阻止您的程序能够更新 UI 或允许用户与 UI 交互。

如果您需要对 UI 进行更改(即显示文件处理的输出),您应该尝试使用SwingWorker.

主要原因是它允许您在另一个线程中执行长时间运行的任务,但提供了将更新重新同步回 EDT 的机制,在 EDT 中对 UI 进行更改是安全的。

查看Swing 中的并发以获取更多详细信息。

无论您采取哪个方向,您都需要提供对正在执行任务的对象的引用并提供某种“取消”标志,“任务”对象需要监视

于 2013-11-08T23:06:30.293 回答
0

好吧,我让它停下来。我想如果你在树林里漫步足够长的时间,你会找到一个侏儒。我上周读了罗宾的暗示,有点放弃了。然后我读的越来越多。然后更多。但罗宾向我保证,这里的树林里确实存在侏儒!

我使用的代码是我为 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 所说的。我几乎觉得我知道我做了什么。

PS 请注意,我已经能够通过invokeLater()--last 几行原始问题获得我的输出。

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

于 2013-11-13T04:25:32.110 回答
0

我昨晚离开这个程序的方式并不令人满意,因为退出导致用户无法看到到目前为止显示的输出(它可能很有用)。所以我建立了窗口侦听器并使用关闭事件将布尔值设置aborted为 true 以防止进一步输出到窗口,但线程继续运行,如果在线程结束之前开始另一个搜索,则会导致间歇性问题。

这是我修复它的方法。

FileVisitor接口有 4 种方法来实现遍历树 - 每个访问的文件两个,每个目录两个。每个返回一个FileVisitResult通常是FileVisitResult.CONTINUE. 通过在文件访问者线程中将返回值更改为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;
  }

我是一个快乐的露营者!谢谢你们的想法和意见。

于 2013-11-14T00:36:20.940 回答