java 如何在组件绘制时创建“请稍候”Swing 对话框
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11746891/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to create "please wait" Swing dialog while component paints
提问by Tom
Still relatively new to Swing but after a couple of hours of search, I couldn't find the answer online, hence this post (sorry if already answered and I overlooked it).
Swing 仍然相对较新,但经过几个小时的搜索,我无法在网上找到答案,因此这篇文章(抱歉,如果已经回答并且我忽略了它)。
I'm using JFreeChart in a Swing application. Some charts are relatively heavy (180k data points) and the JFreeChart's ChartPanel needs ~6 seconds to do its first paintComponent().
我在 Swing 应用程序中使用 JFreeChart。一些图表相对较重(180k 数据点)并且 JFreeChart 的 ChartPanel 需要大约 6 秒来执行它的第一个paintComponent()。
Hence I would like to show a "Please wait" message in a dialog while the component paints (no need to show progress with a SwingWorker). I tried to override the paintComponent method but unfortunately the message never appears on screen (I guess the thread directly goes into painting the chart, without taking the time to paint the dialog).
因此,我想在组件绘制时在对话框中显示“请稍候”消息(无需显示 SwingWorker 的进度)。我试图覆盖paintComponent方法,但不幸的是该消息从未出现在屏幕上(我猜线程直接进入绘制图表,而没有花时间绘制对话框)。
My code looks like this:
我的代码如下所示:
public class CustomizedChartPanel extends ChartPanel{
private static final long serialVersionUID = 1L;
private JDialog dialog = null;
boolean isPainted = false;
public CustomizedChartPanel(JFreeChart chart) { super(chart); }
@Override
public void paintComponent(Graphics g) {
//At first paint (which can be lengthy for large charts), show "please wait" message
if (! isPainted){
dialog = new JDialog();
dialog.setUndecorated(true);
JPanel panel = new JPanel();
panel.add(new JLabel("Please wait"));
dialog.add(panel);
dialog.pack();
GuiHelper.centerDialog(dialog); //Custom code to center the dialog on the screen
dialog.setVisible(true);
dialog.repaint();
}
super.paintComponent(g);
if (! isPainted){
isPainted = true;
dialog.dispose();
super.repaint();
}
}
}
Any pointer on how to solve this / best practices would be very much appreciated!
非常感谢有关如何解决此问题/最佳实践的任何指示!
Thanks, Thomas
谢谢,托马斯
UPDATE:
更新:
Thanks for the hints & debate: very helpful.
感谢您的提示和辩论:非常有帮助。
I started implementing the suggested solution with the invokeLater() as I'm fearing that the JLayer solution won't work since it's also running on the EDT.
我开始使用 invokeLater() 实施建议的解决方案,因为我担心 JLayer 解决方案不起作用,因为它也在 EDT 上运行。
Unfortunately I'm having a null pointer exception when the paintComponent() is called by the invokeLater().
不幸的是,当invokeLater() 调用paintComponent() 时,我遇到了空指针异常。
My code looks like this:
我的代码如下所示:
@Override
public void paintComponent(Graphics graph) {
//At first paint (which can be lengthy for large charts), show "please wait" message
if (! isPainted){
isPainted = true;
dialog = new JDialog();
dialog.setUndecorated(true);
JPanel panel = new JPanel();
panel.add(new JLabel("Please wait"));
panel.add(new JLabel("Please wait !!!!!!!!!!!!!!!!!!!!!!!!!!!!!"));
dialog.add(panel);
dialog.pack();
GuiHelper.centerDialog(dialog); //Custom code to center the dialog on the screen
dialog.setVisible(true);
dialog.repaint();
RunnableRepaintCaller r = new RunnableRepaintCaller(this, graph, dialog);
SwingUtilities.invokeLater(r);
}
else super.paintComponent(graph); //NULL POINTER EXCEPTION HERE (invoked by runnable class)
}
And the runnable class is:
可运行的类是:
public class RunnableRepaintCaller implements Runnable{
private ChartPanel target;
private Graphics g;
private JDialog dialog;
public RunnableRepaintCaller(ChartPanel target, Graphics g, JDialog dialog){
this.target = target;
this.g = g;
this.dialog = dialog;
}
@Override
public void run() {
System.out.println(g);
target.paintComponent(g);
dialog.dispose();
}
}
Again, any pointer would be much appreciated !
再次,任何指针将不胜感激!
Thomas
托马斯
回答by Guillaume Polet
Here is an example and/but it uses SwingWorker. You should seriously consider using this because if somehow the OS invalidates your frame and the loading of your JFreeChart is done on the EDT (Event Dispatching Thread), then your GUI will look frozen.
这是一个示例和/但它使用 SwingWorker。您应该认真考虑使用它,因为如果操作系统以某种方式使您的框架无效并且您的 JFreeChart 的加载是在 EDT(事件调度线程)上完成的,那么您的 GUI 将看起来被冻结。
It also allows you to give better user feedback while you are processing the data. (sorry if the code is a bit long but most of the interesting code is in the initUI and the SwingWorker).
它还允许您在处理数据时提供更好的用户反馈。(抱歉,代码有点长,但大部分有趣的代码都在 initUI 和 SwingWorker 中)。
Note: instead of the dialog, you could use the JLayer (if you use Java 7), but this was unnecessary in my example.
注意:您可以使用 JLayer(如果您使用 Java 7)代替对话框,但这在我的示例中是不必要的。
The code is highly inspired from http://www.vogella.com/articles/JFreeChart/article.html
该代码的灵感来自http://www.vogella.com/articles/JFreeChart/article.html
/**
* This code was directly taken from: http://www.vogella.com/articles/JFreeChart/article.html
* All credits goes to him for this code.
*
* Thanks to him.
*/
import java.util.List;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PiePlot3D;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.data.general.PieDataset;
import org.jfree.util.Rotation;
public class PieChart extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
initUI();
}
});
}
protected static void initUI() {
// First we create the frame and make it visible
final PieChart demo = new PieChart("Comparison");
demo.setSize(500, 270);
demo.setVisible(true);
// Then we display the dialog on that frame
final JDialog dialog = new JDialog(demo);
dialog.setUndecorated(true);
JPanel panel = new JPanel();
final JLabel label = new JLabel("Please wait...");
panel.add(label);
dialog.add(panel);
dialog.pack();
// Public method to center the dialog after calling pack()
dialog.setLocationRelativeTo(demo);
// allowing the frame and the dialog to be displayed and, later, refreshed
SwingWorker<JFreeChart, String> worker = new SwingWorker<JFreeChart, String>() {
@Override
protected JFreeChart doInBackground() throws Exception {
publish("Loading dataset");
// simulating the loading of the Dataset
try {
System.out.println("Loading dataset");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// This will create the dataset
PieDataset dataset = demo.createDataset();
publish("Loading JFreeChart");
// simulating the loading of the JFreeChart
try {
System.out.println("Loading JFreeChart");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// based on the dataset we create the chart
JFreeChart chart = demo.createChart(dataset, "Which operating system are you using?");
// we put the chart into a panel
return chart;
}
@Override
protected void process(List<String> chunks) {
label.setText(chunks.get(0));
dialog.pack();
dialog.setLocationRelativeTo(demo);
dialog.repaint();
}
@Override
protected void done() {
try {
// Retrieve the created chart and put it in a ChartPanel
ChartPanel chartPanel = new ChartPanel(this.get());
// add it to our frame
demo.setContentPane(chartPanel);
// Dispose the dialog.
dialog.dispose();
// We revalidate to trigger the layout
demo.revalidate();
// Repaint, just to be sure
demo.repaint();
} catch (Exception e) {
e.printStackTrace();
}
}
};
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
worker.execute();
}
});
dialog.setVisible(true);
}
public PieChart(String applicationTitle) {
super(applicationTitle);
}
/** * Creates a sample dataset */
private PieDataset createDataset() {
DefaultPieDataset result = new DefaultPieDataset();
result.setValue("Linux", 29);
result.setValue("Mac", 20);
result.setValue("Windows", 51);
return result;
}
/** * Creates a chart */
private JFreeChart createChart(PieDataset dataset, String title) {
JFreeChart chart = ChartFactory.createPieChart3D(title, // chart title
dataset, // data
true, // include legend
true, false);
PiePlot3D plot = (PiePlot3D) chart.getPlot();
plot.setStartAngle(290);
plot.setDirection(Rotation.CLOCKWISE);
plot.setForegroundAlpha(0.5f);
return chart;
}
}
回答by Nitin Chhajer
You can use JLayer
as explained here.
This is specifically for a busy indicator as you want.
您可以JLayer
按照此处的说明使用。这特别适用于您想要的繁忙指标。
Further more you can keep the JPanel
with setEnabled(false)
till your data completely loads. This prevents unwanted clicks on the JPanel
.
进一步可以保持JPanel
与setEnabled(false)
直到你的数据完全负载。这可以防止对JPanel
.
回答by Taedrin
It's been a long time since I've done anything in Java, but as far as I know, the repaint()
method does not actually cause any drawing to happen. It simply flags the control as needing to be redrawn at the soonest possible opportunity. You need to call the paint()
method directly if you want a component to be drawn immediately.
我已经很久没有用 Java 做过任何事情了,但据我所知,该repaint()
方法实际上并没有导致任何绘图发生。它只是将控件标记为需要尽快重新绘制。paint()
如果要立即绘制组件,则需要直接调用该方法。
回答by outellou
You need to start the waiting Dialog in a new Thread. I don't know how you create your chart, but here is a sample
您需要在新线程中启动等待对话框。我不知道你是如何创建图表的,但这里有一个示例
SwingUtilities.invokeLater(new Runnable() {
public void run() {
dialog = new JDialog();
dialog.setUndecorated(true);
JPanel panel = new JPanel();
panel.add(new JLabel("Please wait"));
dialog.add(panel);
GuiHelper.centerDialog(dialog);
dialog.setVisible(true);
Thread performer = new Thread(new Runnable() {
public void run() {
dialog.setVisible(false);
//Here the code that prepare the chart
}
});
performer.start();
}
});