java - 进程运行时在窗口中显示计时器

标签 java swing

所以我当前正在通过以下函数执行脚本:

public static boolean executePythonScriptWithArgs(File file, String args) {
    Runnable r = new Runnable() {
        @Override
        public void run() {
            try {
                Process p = Runtime.getRuntime().exec("python3 " + file.getPath() + " " + args);

                BufferedReader stdInput = new BufferedReader(new
                        InputStreamReader(p.getInputStream()));

                // Read the output from the command
                String s;
                while ((s = stdInput.readLine()) != null) {
                    if(s.equals("success")) {
                        result = true;
                    }
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    };

    ExecutorService executor = Executors.newCachedThreadPool();
    executor.submit(r);

    return result;
}

为了阻止 GUI 卡住,我创建了一个新的 Runnable 对象并执行它来检查结果是否为 true。我正在使用以下命令更改变量结果:

私有(private)静态 volatile boolean 结果 = false;

能够在可运行线程期间更改值是 volatile

但是,这只是在后台运行,我希望打开一个新窗口(如果可以的话,可能使用 JOptionPane)来仅显示计时器,然后将其关闭当达到成功值时。

我怎样才能做到这一点?

编辑:

我正在使用的代码(除了用于容纳面板的 JFrame 之外)如下所示:

MyListener.java

public class MyListener implements ActionListener
{
    public static InputStream getScriptResource(String fileName) {
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        InputStream is = classLoader.getResourceAsStream("scripts/" + fileName);
        return is;
    }

    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == MyPanel.button) {
            String options = "";

            try {
                File tempFile = File.createTempFile("quick", ".py");
                tempFile.deleteOnExit();
                // Get your script and prepare OutputStream to tempFile
                // Try with resources, to close streams
                try (InputStream is = getScriptResource("quick.py"); FileOutputStream out = new FileOutputStream(tempFile)) {
                    // Copy InputStream to OutputStream.
                    IOUtils.copy(is, out);
                }
                boolean result = ScriptExecutor.executePythonScriptWithArgs(tempFile, options);
                if (result) {
                    JOptionPane.showMessageDialog(Client.INSTANCE, "Scan completed successfully!", "Info", JOptionPane.PLAIN_MESSAGE);
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}

MyPanel.java

public class MyPanel extends JPanel
{
    public static JButton button;
    private ActionListener myListener;

    public MyPanel() {
        this.myListener = new MyListener();
        setLayout(new GridLayout(1,1));

        addComponents();

        setVisible(true);
    }

    private void addComponents() {
        button = new JButton("Run Script");
        button.addActionListener(myListener);
        add(button);
    }
}

ScriptExecutor.java

public class ScriptExecutor
{
    private static volatile boolean result = false;

    public static boolean executePythonScriptWithArgs(File file, String args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                try {
                    Process p = Runtime.getRuntime().exec("python3 " + file.getPath() + " " + args);

                    BufferedReader stdInput = new BufferedReader(new
                            InputStreamReader(p.getInputStream()));

                    // Read the output from the command
                    String s;
                    while ((s = stdInput.readLine()) != null) {
                        if(s.equals("success")) {
                            result = true;
                        }
                    }

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        };

        ExecutorService executor = Executors.newCachedThreadPool();
        executor.submit(r);

        return result;
    }
}

此代码可以放入任何要运行的 jframe 中 - 注意:我的环境使用 Maven,并且可以在资源文件夹中找到该脚本。

最佳答案

一种方法:在长时间运行的代码之前声明对话框窗口,但在执行线程之后显示它。然后在线程完成运行后使用回调关闭对话框。上面给出的顺序的原因是——对话框需要对回调可见,因此需要首先声明,但模式对话框会阻塞代码流,因此需要在启动后台线程后显示。

请注意,SwingWorkers 已经为您设置了回调机制,如果需要,您可以使用它。

例如:

import java.awt.BorderLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import javax.swing.*;

@SuppressWarnings("serial")
public class CallBackEgGui extends JPanel {
    private Action startThreadAction = new StartThreadAction(this);
    private JButton startThreadBtn = new JButton(startThreadAction);
    private JTextArea textArea = new JTextArea(15, 30);
    private JProgressBar progressBar = new JProgressBar();

    public CallBackEgGui() {
        progressBar.setIndeterminate(true);

        JPanel btnPanel = new JPanel();
        btnPanel.add(startThreadBtn);

        textArea.setFocusable(false);
        JScrollPane scrollPane = new JScrollPane(textArea);
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

        setLayout(new BorderLayout());
        add(scrollPane);
        add(btnPanel, BorderLayout.PAGE_END);
    }

    public void showText(String text) {
        textArea.append(text + "\n");
    }

    public void startProcess() {
        // disable the JButton
        startThreadAction.setEnabled(false);

        // display a JOptionPane (or any modal JDialog) that can hold anything, including a counter if we want
        // would use a Swing Timer if I wanted to show a Timer
        String title = "Running Python Script";
        int messageType = JOptionPane.PLAIN_MESSAGE;
        JOptionPane.showMessageDialog(CallBackEgGui.this, progressBar, title , messageType);
    }

    public void endProcess(boolean result) {
        // re-enable the JButton
        startThreadAction.setEnabled(true);

        // one way to close the JOptionPane
        Window win = SwingUtilities.getWindowAncestor(progressBar);
        win.dispose();

        // display another JOptionPane that shows the result of the process
        String message = "Python script Success: " + result;
        String title = "Return From Python Script";
        int messageType = JOptionPane.PLAIN_MESSAGE;
        JOptionPane.showMessageDialog(CallBackEgGui.this, message, title , messageType);
    }

    private static void createAndShowGui() {
        CallBackEgGui mainPanel = new CallBackEgGui();

        JFrame frame = new JFrame("LongRunningTimer");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

}

@SuppressWarnings("serial")
class StartThreadAction extends AbstractAction {
    private CallBackEgGui gui;
    private LongRunningCode longRunningCode;

    public StartThreadAction(CallBackEgGui gui) {
        super("Start Long Running Code");
        this.gui = gui;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        longRunningCode = new LongRunningCode(gui);
        longRunningCode.execute(); // start the SwingWorker

        // order is important since the method below shows the modal dialog
        // and thus blocks any code flow below it. So execute the worker FIRST
        // before calling this blocking code
        gui.startProcess(); 
    }
}

class LongRunningCode extends SwingWorker<Boolean, String> {
    private static final int MAX = 10;
    private static final long MILLI_SECONDS_SLEEP = 400;
    private CallBackEgGui gui;

    public LongRunningCode(CallBackEgGui gui) {
        this.gui = gui;
    }

    @Override
    protected Boolean doInBackground() throws Exception {
        // this code is in place of the Python script
        // done within a background thread
        for (int i = 0; i < MAX; i++) {
            TimeUnit.MILLISECONDS.sleep(MILLI_SECONDS_SLEEP);

            // in the real program, the text will be from the InputStream from
            // the Python process.
            String text = "Value: " + i;  
            publish(text);  // then send the text to the GUI
        }

        // occasionally returns false
        boolean returnValue = Math.random() > 0.2;
        return returnValue;
    }

    @Override
    protected void process(List<String> chunks) {
        // this code is called on the Swing event thread
        for (String text : chunks) {
            gui.showText(text); // tell GUI to display text
        }
    }

    @Override
    protected void done() {
        // this code is called on the Swing event thread
        try {
            boolean result = get(); // get the result from the Worker thread
            gui.endProcess(result);  // tell GUI that process is done
        } catch (InterruptedException | ExecutionException e) {
            // TODO handle exception!
            e.printStackTrace();
        }
    }
}

另请查看Lesson: Concurrency in Swing

关于java - 进程运行时在窗口中显示计时器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49707994/

相关文章:

java - 按钮提交时 Wicket 面板不会刷新

java - 禁用 Thread.sleep()

java - 浏览器如何管理 Cookie

java - DefaultTableCellRenderer 内 JTable 中的旧值

java - 如何在 JLabel 上显示 "draw"?

java - 如何在 JTextPane 中搜索特定格式?

java - JTable 中选定的行未突出显示

Java NIO FileChannels 跟踪进度

java - "Exception in thread "主要 "java.lang.ArrayIndexOutOfBoundsException: 178"错误,不知道为什么

java - 程序在 JFrame 高度最大化时不重新绘制