java JComboBox doesn't update (via model) - java

I've created a small application that reads data about university subjects from an XML file.
I can add new subjects, etc.
My problem however is that I have another frame with a JComboBox in it which is filled only via one method, which is called whenever I make a change (either reading from an XML file or adding a subject which is then added to my JTable and the XML file).
When I initially start my application it will automatically open a default XML file and read the contents, and add every subject to the combo box, just as I want. However, subsequent calls (like adding a subject or opening a new file) seem to have no effect, whatsoever.
The code in question:
public void fillComboBoxSubject(ArrayList<Subject> subjectList)
{
DefaultComboBoxModel<String> cbm = new DefaultComboBoxModel<String>();
for ( Subject subject : subjectList )
{
cbm.addElement( subject.getName() ); //getName() returns a String
System.out.println(subject.getName());
}
comboBoxSubject.setModel(cbm);
}
The println will display me every single subject if I open a new file, the combo box is not updated, though.
Regards,
LML
edit: SSCCE
Contains all appearances of the combo box:
public class FEnterMark extends JFrame
{
private JComboBox<String> comboBoxSubject;
public FEnterMark()
{
comboBoxSubject = new JComboBox<String>();
comboBoxSubject.setBounds(83, 8, 140, 20);
contentPane.add(comboBoxSubject);
}
public void fillComboBoxSubject(ArrayList<Subject> subjectList)
{
DefaultComboBoxModel<String> cbm = new DefaultComboBoxModel<String>();
for ( Subject subject : subjectList )
{
cbm.addElement( subject.getName() );
System.out.println(subject.getName());
}
comboBoxSubject.setModel(cbm);
}
}

Related

I need help closing a DialogDisplayer window in a Netbeans Platform application

I am adding to an existing codebase using the Netbeans Platform (14) and its GUI builder to display user selected data to create an output file. The user selects the inputs, then selects to generate the file using a default file name. I want to interrupt the process with a dialog presenting the user with a summary of what they entered, a TextField containing the default file name and the OK - Cancel buttons. I created a DialogDisplayer configured by a DialogDescriptor containing a JPanel which contains the summary info and file name JTextField. This all works, I see the summary data, am able to modify the file name but selecting the OK or Cancel doesn't close the window. Only the X in the upper right will close it.
My actionPerformed() method gets called and exercises the code appropriate to the selected button, but just can't figure out how to close the window from there. I tried setting the closing options to null (dd.setClosingOptions(null);) which the API says causes all action to close the window. No dice.
I don't see a method to call to close the DialogDisplayer window in the API.
I originally thought of using a JDialog but it requires a Frame, which I can't figure out how to get from a org.netbeans.spi.project.ActionProvider, the enclosing class that initiates the request. I have used Swing for more years than I care to admit (since java 1.1) but the Netbeans Platform framework is new to me.
Here is my code:
private class FileNameDialog extends JPanel implements ActionListener
{
private final JTextField fileNameField = new JTextField(50);
private final JLabel fileNameLabel = new JLabel("File Name");
private final JLabel infoLabel = new JLabel();
private final JPanel entryPanel = new JPanel(new FlowLayout());
public FileNameDialog(String fileName, String info)
{
infoLabel.setText(info);
fileNameField.setText(fileName);
setLayout(new BorderLayout());
entryPanel.add(fileNameLabel);
entryPanel.add(fileNameField);
add(BorderLayout.CENTER, infoLabel);
add(BorderLayout.PAGE_END, entryPanel);
}
#Override
public void actionPerformed(ActionEvent e)
{
if (e.getActionCommand().equals(OK_BUTTON))
{
//Replace the file name with what was entered and move on
abort = false; //Global field in enclosing class
logger.log(Level.INFO, "Setting abort to FALSE for {0}",
fileNameField.getText());
}
else if (e.getActionCommand().equals(CANCEL_BUTTON))
{
abort = true; //Global field in enclosing class
logger.log(Level.INFO, "Setting abort to TRUE");
}
//Close the Dialog Window Here (somehow)
}
}
/**
* Request text entry from the user. Currently used for displaying the
* file name and allowing the user to update it. This is the entry point
* for all this code.
* #param info summary text
* #param title window title
* #return the user entered String
*/
private String userRequestDialog(String info, String title, String fileName)
{
FileNameDialog fileNameDialog = new FileNameDialog(fileName, info);
Object [] options = { new JButton ("OK"),
new JButton ("Cancel")};
DialogDescriptor dd = new DialogDescriptor (fileNameDialog,
title,
true,
options,
null,
DialogDescriptor.DEFAULT_ALIGN,
null,
fileNameDialog);
DialogDisplayer.getDefault().notify(dd); //Display the window
dd.setClosingOptions(null); //Doesn't seem to have any effect
return fileNameDialog.fileNameField.getText(); //FileName to use as process continues
}
Just for giggles, I tried Object frame = lookup.lookup(JFrame.class); but that comes back as null.
#jjazzboss - You had the answer that solved my problem and you should get the credit.
Though it technically didn't answer the question, it allowed me to replace the Netbeans DialogDisplayer with a JOptionPane as in below. I also tried a CustomDialog modeled after the one in https://docs.oracle.com/javase/tutorial/uiswing/components/dialog.html, but the OK and Cancel buttons still didn't close it. I suspect that something in Netbeans is stealing those events because a breakpoint in the listener never got hit (or I screwed up the code).
boolean cancelled = false;
Frame frame = WindowManager.getDefault().getMainWindow();
while (!cancelled)
{
String newFileName = JOptionPane.showInputDialog(frame, info, fileName);
if (newFileName == null) //OK was not selected
{
return null;
}
else if (isValidFileName(newFileName))
{
return newFileName;
}
else
{
JOptionPane.showMessageDialog(
frame,
"Sorry, \"" + newFileName + "\" "
+ "isn't a valid file name.\n"
+ "Please Try again",
"Bad File Name",
JOptionPane.ERROR_MESSAGE);
}
}

saving nodes created by user on JavaFX GUI

enter image description hereI made a GUI on JavaFX that's for creating timetables. when I open the app I can add plans (Buttons) to day columns (VBox). ofc the changes aren't saved after I close the app: the next time I open it the table is empty.
my question is how can I make it save nodes that the user creates so the next time i open the app they're there?
this is the exact part where the nodes get added for what it's worth:
void ask_add_plan(ActionEvent event)
{
Button b = (Button) event.getSource();
VBox v = (VBox) b.getParent();
AnchorPane pop_up = new AnchorPane(); //this pop up is to specify things about the plan
//but i removed unnecessary code for simplicity
VBox pop_up_v = new VBox();
Button add = new Button("Add");
add.setOnAction(e->{
Button plan = new Button(what_to_do.getText());
v.getChildren().add(plan);
container.getChildren().remove(pop_up); //container is the anchor pane everything's in
});
pop_up_v.getChildren().add(add);
pop_up.getChildren().add(pop_up_v);
container.getChildren().add(pop_up); //container is the anchor pane everything's in
}
The JavaFX nodes are just the presentation of your data. They are not meant to be saved. Store the actual data itself in a private field in your class.
In your Application.stop method, write the data to a file.
In your Application.start method, read that file and use it to rebuild JavaFX nodes.
private static final Path PLANS_FILE =
Path.of(System.getProperty("user.home"), "plans.txt");
private final List<String> plans = new ArrayList<>();
void ask_add_plan(ActionEvent event) {
// ...
add.setOnAction(e -> {
String planText = what_to_do.getText();
plans.add(planText);
Button plan = new Button(planText);
v.getChildren().add(plan);
container.getChildren().remove(pop_up);
});
}
#Override
public void start(Stage primaryStage)
throws Exception {
// ...
if (Files.exists(PLANS_FILE)) {
plans.addAll(Files.readAllLines(PLANS_FILE));
// Add UI elements for each stored plan.
for (String planText : plans) {
Button planButton = new Button(planText);
v.getChildren().add(planButton);
container.getChildren().remove(pop_up);
}
}
}
#Override
public void stop()
throws IOException {
Files.write(PLANS_FILE, plans);
}
The above is just a simplified example. The file doesn’t have to store just strings.
I get the impression that the data created in the application is more complex than just plan strings. For complex data, XML may be more a suitable file format. Or you can use Java serialization. Or, you can invent your own file format.
You also don’t have to read from and write to a file. You can use a database, if you’re comfortable with database programming. Or you can use Preferences (though Preferences are not well suited to storing lists of complex data).
You should use MVP (model-view-presenter) pattern. Saving data in UI layer is not a good practice. Create a model with data and then serialize it.

Java Swing app trying to populate JComboBox on button click

I am learning Java Swing. I am trying to develop as simple app for learning purpose. There is are multiple issues in following code. I try to read a csv file and populate JComboBox on button click.
public class MyForm {
private JButton btnRead;
private JButton btnRead2;
private JComboBox cbCodes;
private JPanel mainPanel;
private DefaultComboBoxModel comboBoxModel;
public MyForm(){
// issue 1: I always get null pointer exception in this line
comboBoxModel = new DefaultComboBoxModel();
cbCodes = new JComboBox(comboBoxModel);
btnRead.addActionListener( e -> {
List<String[]> data = readData();
comboBoxModel.removeAllElements();
data.forEach(item -> comboBoxModel.addElement(item));
});
// issue 2: Since DefaultComboBoxModel was not working. I tried without it. As this I get correct data in the array. But when I make JComboBox with array. Nothing is filled. It is empty.
btnRead2.addActionListener( e -> {
List<String[]> data = readData();
String[] array = new String[data.size()];
data.toArray(array);
cbCodes = new JComboBox(array);
});
}
// issue 3: I can't complie the code without this empty method. Why do I need it?
// error: Form contains components with Custom Create option but no createUIComponents() method
void createUIComponents(){
}
public List<String[]> readData() {
String file = "data.csv";
List<String[]> content = new ArrayList<>();
try(BufferedReader br = new BufferedReader(new FileReader(file))) {
String line = "";
while ((line = br.readLine()) != null) {
if(line.contains("\"")){
content.add(line.split(" "));
}
content.add(line.split(","));
}
} catch (FileNotFoundException e) {
//Some error logging
} catch (IOException e) {
e.printStackTrace();
}
return content;
}
public static void main(String[] args) {
JFrame frame = new JFrame("MyForm");
frame.setContentPane(new MyForm().mainPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}
I make my question in the source code with the comment to show exactly here those issues are involved.
You don't get the NullPointerException in the lines you indicated, but in the line btnRead.addActionListener( e -> { because btnRead has not been initialized yet!
When you create a new JComboBox, you have to add it to the panel, too. Just creating it with new will not display it. But the real problem behind it is: you're using the model wrong. Write
comboBoxModel.removeAllElements();
for (final String string : array) {
comboBoxModel.addElement(string);
}
to solve that.
The problem you have here does not lie within the code you provided, but from another component. At some point, someone used a UI designer. Those designers usually create initialization methods, just like createUIComponents. See where that method gets called.
Synopsis:
All in all, your code is really chaotic. Restructure from new, this will clean up a lot of problems.
And initialize UI components as soon as possible, best do it in the declaration line: private final JButton btnRead = new JButton("Read!");
I strongly recommend using an IDE like Eclipse or IntelliJ that will help you write clean code and see and correct problems easier.

Add style to CompletionProposal in Eclipse RCP (Content assist)

I'm creating a custom content assist for an editor, this is how I'm creating the proposals:
#Override
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
String test = "Test";
ContextInformation contextInfo = new ContextInformation("Context display test", "information display test");
CompletionProposal proposal = new CompletionProposal(test,
offset,
0,
test.length(),
Activator.getImage("icons/sample.png"),
test,
contextInfo,
"Additional info");
return new ICompletionProposal[] {proposal};
}
This is the result:
Which is fine, but for example, in the content assist of the Java editor, they are using colors such as blue and gray:
I know there is a class called StyledText that could help but I can't find a good example to use it in combination with CompletionProposal.
The extension interface ICompletionProposalExtension6 supports styled display strings. Its sole method getStyledDisplayString() must return a StyledString that is used for display.
Instead of creating an instance of CompletionProposal you would have to implement your own ICompletionProposal that also implements the above mentioned extension. For example:
class StyledCompletionProposal
implements ICompletionProposal, ICompletionProposalExtension6
{
...
#Override
public StyledString getStyledDisplayString() {
return new StyledString("test").append(" [10%]", Styler.QUALIFIER_STYLER);
}
}
In addition, the content assistant must be configured to enable coloured labels. For editors, this is usually done in SourceViewerConfiguration::getContentAssistant:
ContentAssistant contentAssistant = new ContentAssistant();
contentAssistant.enableColoredLabels(true);

OutOfMemoryError and problems with ObservableList into ComboBox

I make an fxml file in javafx2.
I have a List of Person objects. The name of this List, is Entries. I have an ObservableList, myObservableList. Inside of this I want to put labels. Each label must contains a pair of image person and text of his name.
I write this code:
for (int i=0; i<numberOfEntries; i++){
currentEntry = Entries.get(i);
name=currentEntry.getName();
image1 = new Image("file:"+currentEntry.getIcon());
imageView1= new ImageView();
imageView1.setFitHeight(50);
imageView1.setFitWidth(70);
imageView1.setImage(image1);
label = new Label(name, imageView1);
label.setFont(new Font("serif", 32));
myObservableList.add(label);
}
It works ok, but after a few puts of images the JVM gives me the below error message:
Caused by: java.lang.OutOfMemoryError: Java heap space.
This error comes from the code line image1 = new Image("file:"+currentEntry.getIcon());
Finally, I want to put all elements of myObservableList into a ComboBox items. For this reason in Initialize method of java controller I write:
myComboBox.setItems(myObservableList);
ListCell<Label> buttonCell = new ListCell<Label>() {
#Override protected void updateItem(Label item, boolean isEmpty) {
super.updateItem(item, isEmpty);
setText(item==null ? "" : item.getText());
}
};
myComboBox.setButtonCell(buttonCell);
I am sure that I have not enough experience in javafx, and I do not know how must I handle for I have a combobox with pairs of icon and text in the same cell for all items.
I want to express my lot of thanks to Peter Duniho and PakkuDon for their help for improve English in my text.
It is almost always a mistake to use a Node class as the data type for a ComboBox (or for any other control). You should use a class that represents the data only, and register a cell factory to configure how the data are displayed.
In your case, if you include the image in the data, you are likely to run into memory problems. Each image is likely to be represented in memory by a few megabytes. So your data class should hold the image name, and then you can use the cell in the combo box to create an image.
Here's some sample code to give you the idea:
Data class (Person.java):
public class Person {
private final String name ;
private final String imageFileName ;
public Person(String name, String imageFileName) {
this.name = name ;
this.imageFileName = imageFileName ;
}
public String getName() {
return name ;
}
public String getImageFileName() {
return imageFileName ;
}
}
UI code to create ComboBox from a List<Person>:
List<Person> entries = ... ; // populated from DB
ComboBox<Person> comboBox = new ComboBox<>();
comboBox.getItems().addAll(entries);
comboBox.setCellFactory(new Callback<ListView<Person>, ListCell<Person>>() {
#Override
public ListCell<Person> call(ListView<Person> listCell) {
return new ListCell<Person>() {
private final ImageView = new ImageView();
#Override
public void updateItem(Person person, boolean empty) {
super.updateItem(person, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
File imageFile = new File(person.getImageFileName());
String imageUrl = imageFile.toURI().toURL().toExternalForm();
Image image = new Image(imageUrl, 70, 50,
// preserve ratio
true,
// smooth resizing
true,
// load in background
true);
imageView.setImage(image);
setText(person.getName());
setGraphic(imageView);
}
}
};
}
});
You can use the same ListCell implementation for the ComboBox's buttonCell.
The point here is that cells are only created for the visible cells, so the images are loaded "on demand" as the cells are displayed. Using the Image constructor that takes the width and height parameters also reduces the memory footprint, as the Image object can resize as it loads.
Finally, note that it's important to use the flag to load images in the background, which keeps the UI responsive. If you scroll quickly, you'll likely see some images not loaded for a brief while; the cells will repaint appropriately once the image becomes available.

Categories

Resources