为什么 JOptionPane 的确认对话框在单击 Yes/No 按钮时没有采取行动?
Why Confirm Dialog Box of JOptionPane is not taking action on clicking Yes/No button?
初看我的GUI:
背景:
我正在尝试通过 GUI 使用 MongoDB collection 的一些功能 (CRUD)。
首先,用户必须从第一个 ComboBox 中选择一个现有的数据库。当用户选择一个选项时,private void jComboBoxDBNamePopupMenuWillBecomeInvisible(javax.swing.event.PopupMenuEvent evt)
函数将加载该数据库下的所有 collection。这里选择博客。
然后用户将从第二个 ComboBox 的现有 collection 中选择一个 collection。当用户选择 collection private void jComboBoxCollectionNamePopupMenuWillBecomeInvisible(javax.swing.event.PopupMenuEvent evt)
函数时,将调用名为 refreshTable()
的函数来加载该 collection 下的所有文档。这里选择 posts collection.
从第二个 ComboBox 选择选项时,如果选择的 collection 有超过一千个文档,它会要求用户确认他是否真的想要加载文档,因为这可能需要时间或者可能是内存问题。
它的确认将通过 JOptionPane.showConfirmDialog(...)
.
完成
问题:
选择 collection posts 时,会显示对话框。但是点击 Yes 或者 No 没有任何反应。但是为什么?
图片中带有红色下划线的按钮。
代码:
我的public boolean refreshTable()
函数是:
public boolean refreshTable() {
collections = db.getCollection((String) jComboBoxCollectionName.getSelectedItem());
if(collections.count()>1000){
int ret = JOptionPane.showConfirmDialog(this, "The table contains more than thousand row.\nThis may slow down the process and could cause Memory error.Are you sure to continue?","Too Large Collection ("+collections.count()+" Rows)",YES_NO_OPTION, INFORMATION_MESSAGE);
if(ret!=YES_OPTION) return true;
}
//Some irrelevant codes
return false;
}
学习:
我在Google上搜索过,无法解决问题。以下是关于 Whosebug 的一些问题,但我无法从中找到解决方案。
- JOptionPane YES/No Options Confirm Dialog Box Issue -Java
- JoptionPane ShowConfirmDialog
项目库:
我的项目存储库是 here。有需要的可以看看。
只是猜测,因为我们没有您提供的最小示例程序——但是如果在长 运行ning 或 CPU 密集型代码中调用此 JOptionPane,并且如果代码在 Swing 事件线程上是 运行,它将冻结 Swing 事件线程,从而冻结您的 GUI。如果您不注意在后台线程中调用长 运行ning 或 CPU 密集型代码,您将需要这样做,例如使用 SwingWorker。
我查看了您的代码,您正在 EDT 上启动 GUI:
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
// this will be run on the EDT
new UserInterface().setVisible(true);
}
});
因此 Jack 的建议是不必要的,但是您在 EDT 上进行所有数据库调用并且没有遵循 Swing 线程规则,这会冻结您的程序,因此我的建议是您需要遵循的。您首先需要学习 Swing 线程的基础知识,因此我建议您查看此教程:Lesson: Concurrency in Swing
您可以使用两个 SwingWorker 和两个 JPropertyChangeListeners:
- 一个扩展
SwingWorker<Collections, Void>
的 SwingWorker,比如称为 GetCollectionsWorker。我不知道第一个通用参数应该是什么,除了它应该是您的集合变量的任何类型。这个工人会简单地 return db.getCollection(selection);
从它的 doInBackground()
方法。
- 一个 SwingWorker,比如说称为 CreateTableModelWorker,它扩展了
SwingWorker<DefaultTableModel, Void>
并将集合传递到它的构造函数中,并根据集合持有的数据创建您的 DefaultTableModel。
- 一个名为 GetCollectionsListener 的 PropertyChangeListener,它在 CreateTableModelWorker 上侦听,当它完成时,换句话说,它的新值是
SwingWorker.StateValue.DONE
,然后询问用户是否要继续,如果所以,这调用了第二个 SwingWorker。
- 一个 PropertyChangeListener,比如称为 CreateTableModelWorker,它在完成时侦听 CreateTableModelWorker,并将 table 模型放入 JTable。
对于它的价值,我将在下面的代码中沿着这些行在某处实现。同样,对我来说最大的未知数是 collections
变量代表什么类型,因此,第一个 SwingWorker 的通用参数需要修复并从 Collections
更改为您想要的任何类型使用:
// change to a void method
public void refreshTable() {
String selection = (String) jComboBoxCollectionName.getSelectedItem();
// SwingWorker to get collections
GetCollectionsWorker getCollectionsWorker = new GetCollectionsWorker(selection);
getCollectionsWorker.addPropertyChangeListener(new GetCollectionsListener());
getCollectionsWorker.execute(); // run worker on background thread
}
// FIXME: Generic type Collections is wrong -- need to use correct type, whatever type collections is
private class GetCollectionsWorker extends SwingWorker<Collections, Void> {
private String selection;
public GetCollectionsWorker(String selection) {
this.selection = selection;
}
@Override
protected Collections doInBackground() throws Exception {
// do database work here in a background thread
return db.getCollection(selection);
}
}
// class that listens for completion of the GetCollectionsWorker worker
class GetCollectionsListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
// all this is done on the EDT
if (SwingWorker.StateValue.DONE == evt.getNewValue()) {
// if worker is done, first get worker from listener
GetCollectionsWorker worker = (GetCollectionsWorker) evt.getSource();
try {
// then extract the data that it's returning
collections = worker.get();
// then offer user option of continuing or not
if (collections.count() > 1000) {
int ret = JOptionPane.showConfirmDialog(UserInterface.this,
"The table contains more than thousand row.\nThis may slow down the process and could cause Memory error.Are you sure to continue?",
"Too Large Collection (" + collections.count() + " Rows)", YES_NO_OPTION, INFORMATION_MESSAGE);
if (ret != YES_OPTION) {
return;
}
}
// our next worker, one to create table model
CreateTableModelWorker createModelWorker = new CreateTableModelWorker(collections);
// be notified when it is done
createModelWorker.addPropertyChangeListener(new CreateModelListener());
createModelWorker.execute(); // run on background thread
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
// worker to create table model on background thread
class CreateTableModelWorker extends SwingWorker<DefaultTableModel, Void> {
private Collections collections;
public CreateTableModelWorker(Collections collections) {
this.collections = collections;
}
@Override
protected DefaultTableModel doInBackground() throws Exception {
documents = collections.find().into(new ArrayList<Document>());
Set<String> colNames = new HashSet<>();
for (Document doc : documents) {
for (String key : doc.keySet()) {
colNames.add(key);
}
}
columns = colNames.toArray();
Object[][] elements = new Object[documents.size()][columns.length];
int docNo = 0;
for (int i = 0; i < columns.length; i++) {
if (((String) columns[i]).equalsIgnoreCase("_id")) {
_idcol = i;
break;
}
}
for (Document doc : documents) {
for (int i = 0; i < columns.length; i++) {
if (doc.containsKey(columns[i])) {
elements[docNo][i] = doc.get(columns[i]);
}
}
docNo++;
}
DefaultTableModel model = new DefaultTableModel(elements, columns);
return model;
}
}
private class CreateModelListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
// all this is done on the EDT
if (SwingWorker.StateValue.DONE == evt.getNewValue()) {
// if worker is done, first get worker from listener
CreateTableModelWorker worker = (CreateTableModelWorker) evt.getSource();
try {
DefaultTableModel model = worker.get();
jTableResultTable.setModel(model);
UserInterface.this.model = model;
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
这可能是因为 JOptionPane
方法应该在 Swing 的事件调度线程 (EDT) 上调用,而您在另一个线程上调用它。
您应该尝试使用 SwingUtilities
实用方法调用 refreshTable,例如:
SwingUtilities.invokeLater(() -> refreshTable());
初看我的GUI:
背景:
我正在尝试通过 GUI 使用 MongoDB collection 的一些功能 (CRUD)。
首先,用户必须从第一个 ComboBox 中选择一个现有的数据库。当用户选择一个选项时,private void jComboBoxDBNamePopupMenuWillBecomeInvisible(javax.swing.event.PopupMenuEvent evt)
函数将加载该数据库下的所有 collection。这里选择博客。
然后用户将从第二个 ComboBox 的现有 collection 中选择一个 collection。当用户选择 collection private void jComboBoxCollectionNamePopupMenuWillBecomeInvisible(javax.swing.event.PopupMenuEvent evt)
函数时,将调用名为 refreshTable()
的函数来加载该 collection 下的所有文档。这里选择 posts collection.
从第二个 ComboBox 选择选项时,如果选择的 collection 有超过一千个文档,它会要求用户确认他是否真的想要加载文档,因为这可能需要时间或者可能是内存问题。
它的确认将通过 JOptionPane.showConfirmDialog(...)
.
问题:
选择 collection posts 时,会显示对话框。但是点击 Yes 或者 No 没有任何反应。但是为什么?
图片中带有红色下划线的按钮。
代码:
我的public boolean refreshTable()
函数是:
public boolean refreshTable() {
collections = db.getCollection((String) jComboBoxCollectionName.getSelectedItem());
if(collections.count()>1000){
int ret = JOptionPane.showConfirmDialog(this, "The table contains more than thousand row.\nThis may slow down the process and could cause Memory error.Are you sure to continue?","Too Large Collection ("+collections.count()+" Rows)",YES_NO_OPTION, INFORMATION_MESSAGE);
if(ret!=YES_OPTION) return true;
}
//Some irrelevant codes
return false;
}
学习:
我在Google上搜索过,无法解决问题。以下是关于 Whosebug 的一些问题,但我无法从中找到解决方案。
- JOptionPane YES/No Options Confirm Dialog Box Issue -Java
- JoptionPane ShowConfirmDialog
项目库:
我的项目存储库是 here。有需要的可以看看。
只是猜测,因为我们没有您提供的最小示例程序——但是如果在长 运行ning 或 CPU 密集型代码中调用此 JOptionPane,并且如果代码在 Swing 事件线程上是 运行,它将冻结 Swing 事件线程,从而冻结您的 GUI。如果您不注意在后台线程中调用长 运行ning 或 CPU 密集型代码,您将需要这样做,例如使用 SwingWorker。
我查看了您的代码,您正在 EDT 上启动 GUI:
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
// this will be run on the EDT
new UserInterface().setVisible(true);
}
});
因此 Jack 的建议是不必要的,但是您在 EDT 上进行所有数据库调用并且没有遵循 Swing 线程规则,这会冻结您的程序,因此我的建议是您需要遵循的。您首先需要学习 Swing 线程的基础知识,因此我建议您查看此教程:Lesson: Concurrency in Swing
您可以使用两个 SwingWorker 和两个 JPropertyChangeListeners:
- 一个扩展
SwingWorker<Collections, Void>
的 SwingWorker,比如称为 GetCollectionsWorker。我不知道第一个通用参数应该是什么,除了它应该是您的集合变量的任何类型。这个工人会简单地 returndb.getCollection(selection);
从它的doInBackground()
方法。 - 一个 SwingWorker,比如说称为 CreateTableModelWorker,它扩展了
SwingWorker<DefaultTableModel, Void>
并将集合传递到它的构造函数中,并根据集合持有的数据创建您的 DefaultTableModel。 - 一个名为 GetCollectionsListener 的 PropertyChangeListener,它在 CreateTableModelWorker 上侦听,当它完成时,换句话说,它的新值是
SwingWorker.StateValue.DONE
,然后询问用户是否要继续,如果所以,这调用了第二个 SwingWorker。 - 一个 PropertyChangeListener,比如称为 CreateTableModelWorker,它在完成时侦听 CreateTableModelWorker,并将 table 模型放入 JTable。
对于它的价值,我将在下面的代码中沿着这些行在某处实现。同样,对我来说最大的未知数是 collections
变量代表什么类型,因此,第一个 SwingWorker 的通用参数需要修复并从 Collections
更改为您想要的任何类型使用:
// change to a void method
public void refreshTable() {
String selection = (String) jComboBoxCollectionName.getSelectedItem();
// SwingWorker to get collections
GetCollectionsWorker getCollectionsWorker = new GetCollectionsWorker(selection);
getCollectionsWorker.addPropertyChangeListener(new GetCollectionsListener());
getCollectionsWorker.execute(); // run worker on background thread
}
// FIXME: Generic type Collections is wrong -- need to use correct type, whatever type collections is
private class GetCollectionsWorker extends SwingWorker<Collections, Void> {
private String selection;
public GetCollectionsWorker(String selection) {
this.selection = selection;
}
@Override
protected Collections doInBackground() throws Exception {
// do database work here in a background thread
return db.getCollection(selection);
}
}
// class that listens for completion of the GetCollectionsWorker worker
class GetCollectionsListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
// all this is done on the EDT
if (SwingWorker.StateValue.DONE == evt.getNewValue()) {
// if worker is done, first get worker from listener
GetCollectionsWorker worker = (GetCollectionsWorker) evt.getSource();
try {
// then extract the data that it's returning
collections = worker.get();
// then offer user option of continuing or not
if (collections.count() > 1000) {
int ret = JOptionPane.showConfirmDialog(UserInterface.this,
"The table contains more than thousand row.\nThis may slow down the process and could cause Memory error.Are you sure to continue?",
"Too Large Collection (" + collections.count() + " Rows)", YES_NO_OPTION, INFORMATION_MESSAGE);
if (ret != YES_OPTION) {
return;
}
}
// our next worker, one to create table model
CreateTableModelWorker createModelWorker = new CreateTableModelWorker(collections);
// be notified when it is done
createModelWorker.addPropertyChangeListener(new CreateModelListener());
createModelWorker.execute(); // run on background thread
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
// worker to create table model on background thread
class CreateTableModelWorker extends SwingWorker<DefaultTableModel, Void> {
private Collections collections;
public CreateTableModelWorker(Collections collections) {
this.collections = collections;
}
@Override
protected DefaultTableModel doInBackground() throws Exception {
documents = collections.find().into(new ArrayList<Document>());
Set<String> colNames = new HashSet<>();
for (Document doc : documents) {
for (String key : doc.keySet()) {
colNames.add(key);
}
}
columns = colNames.toArray();
Object[][] elements = new Object[documents.size()][columns.length];
int docNo = 0;
for (int i = 0; i < columns.length; i++) {
if (((String) columns[i]).equalsIgnoreCase("_id")) {
_idcol = i;
break;
}
}
for (Document doc : documents) {
for (int i = 0; i < columns.length; i++) {
if (doc.containsKey(columns[i])) {
elements[docNo][i] = doc.get(columns[i]);
}
}
docNo++;
}
DefaultTableModel model = new DefaultTableModel(elements, columns);
return model;
}
}
private class CreateModelListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
// all this is done on the EDT
if (SwingWorker.StateValue.DONE == evt.getNewValue()) {
// if worker is done, first get worker from listener
CreateTableModelWorker worker = (CreateTableModelWorker) evt.getSource();
try {
DefaultTableModel model = worker.get();
jTableResultTable.setModel(model);
UserInterface.this.model = model;
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
这可能是因为 JOptionPane
方法应该在 Swing 的事件调度线程 (EDT) 上调用,而您在另一个线程上调用它。
您应该尝试使用 SwingUtilities
实用方法调用 refreshTable,例如:
SwingUtilities.invokeLater(() -> refreshTable());