[英]How do I implement the codes which works as a timer app using awt,swing,Thread while satisfying some conditions?
[英]Swing: How do I run a job from AWT thread, but after a window was layed out?
我完整的GUI在AWT线程中运行,因为我使用SwingUtilities.invokeAndWait(...)
启动了主窗口。
现在,我有了一个JDialog,它仅显示一个JLabel
,它指示某个作业正在进行中,并在作业完成后关闭该对话框。
问题是:未显示标签。 这项工作似乎在JDialog
完全布局之前就开始了。
当我只是让对话,而无需等待工作,关闭打开的, 则显示的标签。
对话框在其ctor中所做的最后一件事是setVisible(true)
。
诸如revalidate()
, repaint()
……之类的方法也无济于事。
即使我为受监控的作业启动线程,并使用someThread.join()
等待它,也无济于事,因为我猜想当前线程(即AWT线程)被join
阻塞了。
用JFrame
替换JDialog
也无济于事。
那么,这个概念通常是错误的吗? 或者,在确保JDialog
(或JFrame
)已完全布局后 ,我可以管理它完成某些工作吗?
我要实现的简化算法:
JDialog
的子类 我设法写了一个可重现的测试用例:
编辑现在解决了一个答案的问题:该用例确实显示了标签,但是由于对话框的模态,它在“模拟过程”之后无法关闭。
import java.awt.*;
import javax.swing.*;
public class _DialogTest2 {
public static void main(String[] args) throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
final JLabel jLabel = new JLabel("Please wait...");
@Override
public void run() {
JFrame myFrame = new JFrame("Main frame");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(750, 500);
myFrame.setLocationRelativeTo(null);
myFrame.setVisible(true);
JDialog d = new JDialog(myFrame, "I'm waiting");
d.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
d.add(jLabel);
d.setSize(300, 200);
d.setLocationRelativeTo(null);
d.setVisible(true);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000); // simulate process
jLabel.setText("Done");
} catch (InterruptedException ex) {
}
}
});
d.setVisible(false);
d.dispose();
myFrame.setVisible(false);
myFrame.dispose();
}
});
}
}
尝试这个:
package javaapplication3;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class Main {
public static void main(String[] args)
throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
final JLabel jLabel = new JLabel("Please wait...");
@Override
public void run() {
JFrame myFrame = new JFrame("Main frame");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(750, 500);
myFrame.setLocationRelativeTo(null);
myFrame.setVisible(true);
JDialog d = new JDialog(myFrame, "I'm waiting");
d.add(jLabel);
d.setSize(300, 200);
d.setLocationRelativeTo(null);
d.setVisible(true);
new Thread(new Runnable() {
@Override
public void run() {
public void run() {
try {
Thread.sleep(3000); // simulate process
jLabel.setText("Done"); // HERE: should be done on EDT!
} catch (InterruptedException ex) {
}
}
}).start();
}
});
}
}
这是可行的,但这是不正确的。 我会解释发生了什么。
您的main()
方法从“ main”线程开始。 所有与Swing相关的代码都应在EDT线程上完成。 这就是为什么您(正确)使用SwingUtilities.invokeAndWait(...)
。 到现在为止还挺好。
但是,EDT上不应有长期运行的任务。 由于Swing是单线程的,因此任何长时间运行的进程都会阻塞EDT。 因此,永远不要在EDT上执行您的代码Thread.wait(...)
。 这是我的修改。 我将调用包装在另一个线程中。 因此,这是Swing惯用的长期运行任务处理。 为了简洁起见,我使用Thread类,但我确实建议您使用SwingWorker
线程。
非常重要:在前面的示例中,我犯了一个错误。 看到带有“ HERE”注释的行? 这是另一个Swing单线程规则冲突。 线程内的代码在EDT 外部运行,因此永远不要触摸Swing。 因此,此代码对于Swing单线程规则不正确。 冻结GUI是不安全的。
如何纠正呢? 简单。 您应该将调用包装在另一个线程中,并将其放在EDT队列中。 因此正确的代码应如下所示:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
jLabel.setText("Done");
}
});
编辑 :这个问题触及很多Swing相关问题。 无法一次全部解释...但是下面是另一个片段,它可以满足您的需求:
public static void main(String[] args)
throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
final JFrame myFrame = new JFrame("Main frame");
final JLabel jLabel = new JLabel("Please wait...");
final JDialog d = new JDialog(myFrame, "I'm waiting");
@Override
public void run() {
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(750, 500);
myFrame.setLocationRelativeTo(null);
myFrame.setVisible(true);
d.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
d.add(jLabel);
d.setSize(300, 200);
d.setLocationRelativeTo(null);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000); // simulate process
System.out.println("After");
SwingUtilities.invokeLater(new Runnable() {
public void run() {
d.setVisible(false);
d.dispose();
myFrame.setVisible(false);
myFrame.dispose();
}
});
} catch (InterruptedException ex) {
}
}
}).start();
d.setVisible(true);
}
});
}
总结一下:
SwingUtilities.
在EDT上运行代码SwingUtilities.
...
invokeAndWait()
-顾名思义,调用是同步的, invokeLater()
-调用代码“有时”,但立即返回 Thread
(将Runnable传递给新线程或覆盖它的start()
方法)并启动它, SwingWorker
线程。 典型的GUI场景涉及:
1.,2、3和4.在EDT上运行。 4.不应该。 有许多方法可以编写适当的线程代码。 最麻烦的是使用Java早期版本附带的Thread
类。 如果天真地这样做,则会浪费资源(一次运行的线程太多)。 另外,更新GUI也很麻烦。 使用SwingWorker可以稍微减轻问题。 在启动,运行和更新GUI时,可以保证其行为正常(每个都有专用的方法,您可以重写并确保它在正确的线程上运行)。
如前面的回答所述,您必须在与EDT不同的线程中运行冗长的作业。
最简单的恕我直言是使用SwingWorker并添加一个侦听器以在完成后放置对话框。 这是一个示例:
SwingWorkerCompletionWaiter.java
public class SwingWorkerCompletionWaiter implements PropertyChangeListener {
private JDialog dialog;
public SwingWorkerCompletionWaiter(JDialog dialog) {
this.dialog = dialog;
}
@Override
public void propertyChange(PropertyChangeEvent event) {
if ("state".equals(event.getPropertyName())
&& SwingWorker.StateValue.DONE == event.getNewValue()) {
dialog.setVisible(false);
dialog.dispose();
}
}
}
以下代码将运行一些冗长的任务:
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
// do something long
}
};
JDialog dialog = new JDialog();
// initialize the dialog here
worker.addPropertyChangeListener(new SwingWorkerCompletionWaiter(dialog));
worker.execute();
dialog.setVisible(true);
而且,如果您想创建某种“请稍候”对话框,则只需扩展JDialog并在需要的地方使用扩展的类可能会更容易。
首先,很抱歉对工作有所猜测,在发布代码之前,我并没有真正理解您要执行的操作。
尽管如此,我对创建对话框,窗口和常规对话框的方式并不陌生,因此建议您为JDialog创建一个单独的类。 只是一个简单的类,就像这样:
public class MyDialog extends JDialog
{
public MyDialog(JFrame owner, String title)
{
super(owner, title);
d.setSize(300, 200);
d.setLocationRelativeTo(null);
d.setVisible(true);
d.setDefaultCloseOperation(DISPOSE_ON_CLOSE );
Container c = getContentPane();
c.setLayout(new FlowLayout());
c.add(new JLabel("Please wait..."));
}
}
并以这种方式启动对话框:
new MyDialog(this, "Your title");
它可能看起来像狗屎,但可能有效(我尚未测试)。
希望这可以帮助。
您需要在后台线程或主应用程序线程中运行作业。 应该使用SwingUtilities.invokeLater();
在事件调度线程(EDT)上创建JDialog和JFrame SwingUtilities.invokeLater();
。 然后等待作业完成,然后关闭(EDT)中的对话框。 由于布局和作业位于两个单独的线程上,因此应该没有问题。 实际上,对示例的以下修改有效:
public static void main(String[] args) throws Exception {
final JFrame myFrame = new JFrame("Main frame");
final JDialog d = new JDialog(myFrame, "I'm waiting");
final Thread backgroundJob = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000); // simulate process
}
catch (InterruptedException ex) {
Logger.getLogger(NewClass.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
backgroundJob.start();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(750, 500);
myFrame.setLocationRelativeTo(null);
myFrame.setVisible(true);
d.add(new JLabel("Please wait..."));
d.setSize(300, 200);
d.setLocationRelativeTo(null);
d.setVisible(true);
}
});
backgroundJob.join();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
d.setVisible(false);
}
});
}
您应该使用invokeLater代替InvokeAndWait。
public class DialogTest {
public static void main(String[] args) throws Exception {
final JLabel comp = new JLabel("the job has started");;
final JFrame myFrame = new JFrame("Main frame");
final JDialog d = new JDialog(myFrame, "I'm waiting");
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(750, 500);
myFrame.setLocationRelativeTo(null);
myFrame.setVisible(true);
d.setModalityType(JDialog.ModalityType.APPLICATION_MODAL);
d.add(comp);
d.setSize(300, 200);
d.setLocationRelativeTo(null);
d.setVisible(true);
}
});
try {
Thread.sleep(3000);
// d.setVisible(false);
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
comp.setText("the job has finished");
d.setVisible(false);
d.dispose();
myFrame.setVisible(false);
myFrame.dispose();
}
});
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.