所以我现在通过以下功能执行脚本:
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
对象并执行它来检查结果是否为真。我正在使用以下内容更改变量结果:
private static volatile boolean result = 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,脚本可以在资源文件夹中找到。
答案 0 :(得分:1)
一种方法:在长时间运行的代码之前声明对话框窗口,但在执行线程后显示。然后在线程完成运行后使用回调来关闭对话框。上面给出的顺序的原因 - 对话框需要对回调可见,因此需要先声明,但模态对话框将阻止代码流,因此需要在启动后台线程后显示。
请注意,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();
}
}
}