Reputation: 5445
At first see my GUI:
I am trying to use some functions (CRUD) of MongoDB collection by a GUI.
At first user has to choose an existing Database from the very first ComboBox. When user chooses an option private void jComboBoxDBNamePopupMenuWillBecomeInvisible(javax.swing.event.PopupMenuEvent evt)
function would load all the collections under that database. Here blog is chosen.
Then user would choose a collection among the existing collections from second ComboBox. When user chooses a collection private void jComboBoxCollectionNamePopupMenuWillBecomeInvisible(javax.swing.event.PopupMenuEvent evt)
function would call a function named refreshTable()
to load all the Documents under that collection. Here posts collection is chosen.
While choosing option from second ComboBox, if the chosen collection have more than thousand Documents, it would ask user to confirm whether he actually wants to load the Documents or not as it might take time or could be a memory issue.
It confirmation would be done through a JOptionPane.showConfirmDialog(...)
.
While choosing collection posts, it shows the Dialog Box. But clicking on Yes or No does not gives any response. But why?
The buttons are red underlined in the picture.
My public boolean refreshTable()
function is:
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;
}
I have searched it on Google and could not solve the issue. The followings are some questions on StackOverflow, but I could not figure out solution from them.
My Project repository is here. You could have a look if needed.
Upvotes: 1
Views: 930
Reputation: 285415
A guess only since we don't have a minimal example program from you -- but if this JOptionPane is called amidst long-running or CPU-intensive code, and if the code is run on the Swing event thread, it will freeze the Swing event thread and thus freeze your GUI. If you're not taking care to call long-running or CPU-intensive code within background threads, you will want to do so, such as by use of a SwingWorker.
I looked at your code, and you're starting your GUI on the EDT:
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
// this will be run on the EDT
new UserInterface().setVisible(true);
}
});
and so Jack's recommendation is unnecessary, but you're making all your database calls on the EDT and are not following Swing threading rules, something that will freeze your pogram, and so my recommendations are what you need to follow. You will first need to learn the basics of Swing threading, and so I recommend that you look at this tutorial: Lesson: Concurrency in Swing
You could get by with two SwingWorkers and two JPropertyChangeListeners:
SwingWorker<Collections, Void>
. I have no idea what the first generic parameter should be other than it should be whatever type your collections variable is. This worker would simply return db.getCollection(selection);
from its doInBackground()
method.SwingWorker<DefaultTableModel, Void>
and that is passed collections into its constructor and that creates your DefaultTableModel from the data held by Collections.SwingWorker.StateValue.DONE
, and then that asks the user if he wants to continue, and if so, this calls the second SwingWorker. For what it's worth, I'd implement somewhere along these lines in the code below. Again, the big unknown for me is what type the collections
variable represents, and for that reason, the first SwingWorker's generic parameter would need to be fixed and changed from Collections
to whatever it is that you're using:
// 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();
}
}
}
}
Upvotes: 2
Reputation: 133557
This is probably because JOptionPane
methods should be called on the event dispatch thread (EDT) of Swing while you are inovoking it on a different thread.
You should try calling refreshTable by using SwingUtilities
utility methods, eg:
SwingUtilities.invokeLater(() -> refreshTable());
Upvotes: 3