OutOfMemoryError and problems with ObservableList into ComboBox - java

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.

Related

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.

SWT TableViewer cell does not display image unless clicked on after update

I'm currently working with an SWT TableViewer. And I have a given it a StyledCellLabelProvider implementation. I have given it a set of input and it displays it fine. There is use case where a use can select rows from the table to perform certain actions on and then an icon needs to be added to a specific cell on each row that the modification was done on.
The problem I'm running into here is that the image is not showing up unless I click physically inside the tableviewer after performing the modifcation. Please note that the tableviewer's selection remains the same after the modification has been performed as well. I know the update in the StyledCellLabelProvider is being called after I call refresh on the tableviewer.
My question is why is it not showing up without clicking?
MyStyledCellLabelProvider
public void update(ViewerCell cell) {
if (cell.getElement() instanceof Foo && ((Foo)cell.getElement()).hasBeenModified()) {
cell.setImage(myImage);
}
cell.setText("ABCD");
}
TableViewerComposite
#Override
public void notifyModificationMade(Object[] modifiedObjects){
//Update input on tableviewer
...
//Refresh after updating
tableViewer.refresh(); //Triggers the StyledCellLabelProvider to be called
}
Thanks again!
EDIT:
So one piece of information I forgot is that the cell that is being updated will sometimes have a org.eclipse.ui.forms.widgets.Hyperlink already existing inside of it instead of text and will continue to have it after the image has been added. I think this is what is causing the issue. When I removed the hyperlink code and went back to text, it worked as intended. Here is the code I'm using to add the HyperLink to the cell.
public void update(ViewerCell cell) {
if (cell.getElement() instanceof Foo && ((Foo)cell.getElement()).hasBeenModified()) {
cell.setImage(myImage);
final Hyperlink link = new Hyperlink((Composite) cell.getViewerRow().getControl(),
SWT.UNDERLINE_LINK);
link.setUnderlined(true);
link.setBackground(link.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
link.setForeground(link.getDisplay().getSystemColor(SWT.COLOR_BLUE));
link.setText("My url");
link.setHref(url);
link.addHyperlinkListener(new HyperlinkAdapter() {
...
});
TableItem item = (TableItem) cell.getItem();
TableEditor editor = new TableEditor(item.getParent());
editor.grabHorizontal = true;
editor.grabVertical = true;
editor.setEditor(link, item, cell.getColumnIndex());
editor.layout();
}
}
You need to call super.update(cell); after your code inside update
From the source code of StyledCellLabelProvider.java, the update method says
/*
* (non-Javadoc)
*
* #see org.eclipse.jface.viewers.OwnerDrawLabelProvider#update(org.eclipse.jface.viewers.ViewerCell)
*/
public void update(ViewerCell cell) {
// clients must override and configure the cell and call super
super.update(cell); // calls 'repaint' to trigger the paint listener
}
The super class OwnerDrawLabelProvider.java forces a redraw of cell.

Vaadin 7 ComboBox rendering

So I am experiencing some odd behaviour using Vaadin 7 and the ComboBox component. Essentially, what is happening is that when it first renders the form, it appears to neither have selected the null selection nor any of the items added.
I have attempted to recreate this behaviour with the following code and this demonstrates the issue.
#Override
protected void init(VaadinRequest request) {
final FieldGroup binder;
FormLayout form = new FormLayout();
form.setMargin(true);
setSizeFull();
setContent(form);
final Label output = new Label();
form.addComponent(output);
ComboBox box = new ComboBox("My Dropdown");
final PropertysetItem fields = new PropertysetItem();
fields.addItemProperty("country", new ObjectProperty<String>(""));
binder = new FieldGroup(fields);
binder.bind(box, "country");
box.addItem("aus");
box.setItemCaption("aus", "Australia");
box.addItem("uk");
box.setItemCaption("uk", "United Kingdom");
box.setRequired(true);
box.setImmediate(true);
box.setRequiredError("Country is required field");
Button submit = new Button("Submit", new ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
try {
binder.commit();
output.setValue((String) fields.getItemProperty("country").getValue());
}
catch (CommitException e) {
Notification.show("fail!");
}
}
});
form.addComponent(box);
form.addComponent(submit);
}
By default the ComboBox has allow null selection set to true. So there is a blank entry, which represents a null value selection. However the ComboBox's value when first rendered neither represents the null selection nor one of the items but an empty string.
So when I load the form and click the button the outcome is neither a failure, which it should be because I haven't selected anything yet, nor one of my selections.
This is causing an issue for me in a more advanced UI application but very much the same thing going on here.
Could anybody enlighten me as to what is happening here?
Many thanks,
Joe
So when I load the form and click the button the outcome is neither a
failure, which it should be because I haven't selected anything yet,
nor one of my selections.
Combobox is not empty as you think. It has default property value, that you set as empty string:
fields.addItemProperty("country", new ObjectProperty<String>(""));
Thus form pass validation, because empty string is also a value and empty string != null.
Change this row:
fields.addItemProperty("country", new ObjectProperty<String>(""));
to:
fields.addItemProperty("country", new ObjectProperty<String>(null, String.class));
box.setNullRepresentation("-- Select Country --");

java JComboBox doesn't update (via model)

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);
}
}

how to export a graph to svg/graphml

I am little bit stuck on how to export graphs to svg or graphml. Neither the api, examples or threads on forum.jgraph.com did help me until now.
I need to export graphs to both svg and graphml. I got svg to display the nodes and edges even with the correct layout, but I'm missing information like names of nodes and assigned colors.
With graphml I have no clue yet how to get the correct xml code to even display a functioning graph.
Is there any guideline/workflow somewhere which might help me with export in JGraphX?
Thanks in advance for any help,
Chris
In order to save your graph you have to call mxCellRendered to render your graph to a mxicanvas from which you get your Document (dom document).
From the Renderer it goes like this:
mxGraph.drawCell() -> mxGraph.drawState() -> mxICanvas.drawCell() -> mxICanvas.drawShape()
mxICanvas knows only the cell's geometry and style.
I wanted the cell id attribute added in the svg file as well, so I did the following
extended the mxGraph to override the drawCell() in order to add the cell id in the style of the cell, and
extended the mxSvgCanvas for adding the id attribute for the shapes that interested me
The function to save as svg graph goes like:
// use the extended svg canvas, where the cell id is added as attribute
public void createSVG(mxGraphExtended g) {
String filename = "\home\koula\graph.svg";
mxSvgCanvasExtended canvas = (mxSvgCanvasExtended) mxCellRenderer.drawCells(
g, null, 1, null, new CanvasFactory() {
public mxICanvas createCanvas(int width, int height) {
mxSvgCanvasExtended canvas = new mxSvgCanvasExtended(mxDomUtils
.createSvgDocument(width, height));
canvas.setEmbedded(true);
return canvas;
}
});
try {
mxUtils.writeFile(mxXmlUtils.getXml(canvas.getDocument()), filename);
} catch (IOException e) {
e.printStackTrace();
}
}
The overriden drawCell():
public class mxGraphExtended extends mxGraph {
#Override
public void drawCell(mxICanvas canvas, Object cell) {
// add the cell's id as a style attribute
// cause canvas only get the style and geometry
mxCellState state = this.getView().getState(cell);
state.getStyle().put("cellid", ((mxCell)cell).getId());
super.drawCell(canvas, cell);
}
}
The overridden drawShape() goes like:
public class mxSvgCanvasExtended extends mxSvgCanvas {
//... have coppied only the related code
#Override
public Element drawShape(int x, int y, int w, int h,
Map<String, Object> style)
{
//...
// Draws the shape
String shape = mxUtils.getString(style, mxConstants.STYLE_SHAPE, "");
String cellid = mxUtils.getString(style, "cellid", "");
Element elem = null;
// ...
// e.g. if image, add the cell id
if (shape.equals(mxConstants.SHAPE_IMAGE)) {
String img = getImageForStyle(style);
if (img != null) {
// Vertical and horizontal image flipping
boolean flipH = mxUtils.isTrue(style,
mxConstants.STYLE_IMAGE_FLIPH, false);
boolean flipV = mxUtils.isTrue(style,
mxConstants.STYLE_IMAGE_FLIPV, false);
elem = createImageElement(x, y, w, h, img,
PRESERVE_IMAGE_ASPECT, flipH, flipV, isEmbedded());
/* so here we are */
// add the cell id atribute
if(!cellid.equals("")) {
elem.setAttribute("id", cellid);
}
}
} else if (shape.equals(mxConstants.SHAPE_LINE))
// ...
}// end drawShape
} // end class
Hope this help.

Categories

Resources