It's not the most efficient code for sure, as so far its bare bones
seeing if the app works at all. I already updated it a bit, adding the
ImageView as a field of the cell factory, but it made little
difference. I'll work up a full example tomorrow with pre-loaded images
and everything (and without my util helpers) so others can try.
--John
On 12/07/2023 00:58, Kevin Rushforth wrote:
What you have should work, but will create redundant images and
ImageView ojbects. The recommendation is to create the nodes
(ImageView in this case) in the constructor of the cell factory and
call setGraphic with either null or that factory's node.
Having said that, I doubt that this is related to the visual problems
you are seeing. Maybe Ajit or Andy have some ideas.
-- Kevin
On 7/11/2023 3:50 PM, John Hendrikx wrote:
I tend to avoid ListView, because for some reason it seems to just
never do what I want unless used for its mundane rows of text use
case. I so far always just implemented my own skin for ListView that
deals with displays that have many images that need to scroll
smoothly, but...
Recently, I've again attempted to use ListView, this time for showing
(so far) fixed size ImageViews with images exactly 512x512 pixels in
size:
The cell factory looks like this, which I think is a correct
implementation:
listView.setCellFactory(lv -> {
return new ListCell<>() {
protected void updateItem(ImageHandle imageHandle, boolean
empty) {
if(empty) {
setGraphic(null);
}
else {
try {
setGraphic(new ImageView(new Image(new
ByteArrayInputStream(imageHandle.getImageData()))));
}
catch(IOException e) {
e.printStackTrace();
}
}
}
};
});
Now, this is with JavaFX 21-ea-24 (but also happens on 19). I
spotted the following IMHO bugs:
1) When the ImageView is so large that a single row is only partially
visible, the scrollbar is incorrect (it shows a full bar, even if
half of a row is displayed).
2) The vertical scrollbar doesn't respond to a click in the non-thumb
area at all (you'd expect to make it scroll a page up or down that way)
3) When jerkily resizing the window, I can sometimes have a vertical
+ horizontal scrollbar when there's a full row visible + anywhere
from 0 to 10 extra empty pixels (probably the potential height of a
horizontal scrollbar) with the vertical scroll bar still visible.
The algorithm to hide the scrollbar apparently is not deterministic
and depends on mouse movement speed.
4) When a row is less than half visible, scrolling the window down to
another ImageView will jump the view and hide the first cell that
should still be visible for the bottom 0-49% of its height.
5) It's possible to get the vertical scroll bar in such a state that
it doesn't respond to the mouse any more at all, even by dragging the
thumb. Some keyboard action and mouse scrolling sometimes restores
it to relatively normal functioning.
6) Mouse wheel scrolling acts weird. With one large cell visible and
two rows available, scrolling only **down** on the mouse wheel will
scroll down, then jump back up, and scroll down again... it's
impossible to scroll all the way down to get to a point where it
stops scrolling as it keeps jumping back.
This is with ImageView's. I suppose they're known to have
problems... but then I switched to a Region, which contain an
ImageView with proper min/pref/max implementations. This improved
the situation somewhat. The scrollbar was much more reliable, but I
still saw almost all of the above issues, but somewhat more reliable
than before. Still rows would disappear when (for the most part)
invisible, those and the jumps of the view are just totally
unacceptable.
I'm not quite sure what to make of this, it seems the control is not
meant for arbitrary graphics.
--John
package org.int4.sdui.ui;
import hs.mediasystem.util.image.ImageHandle;
import hs.mediasystem.util.javafx.control.Containers;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.List;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextArea;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.stage.Stage;
import kong.unirest.core.Unirest;
public class Main {
public static void main(String[] args) {
Application.launch(UI.class, args);
}
public static class UI extends Application {
@Override
public void start(Stage primaryStage) {
TextArea prompt = new TextArea("a flower on Mars");
Button button = new Button("Submit");
ListView<ImageHandle> listView = new ListView<>();
listView.setCellFactory(lv -> {
return new ListCell<>() {
protected void updateItem(ImageHandle imageHandle, boolean
empty) {
if(empty) {
setGraphic(null);
}
else {
try {
setGraphic(new ImageView(new Image(new
ByteArrayInputStream(imageHandle.getImageData()))));
}
catch(IOException e) {
e.printStackTrace();
}
}
}
};
});
button.setOnAction(e -> {
Txt2ImgResponse response = textToImage(prompt.getText());
for(String image : response.images) {
listView.getItems().add(new
InMemoryImageHandle(Base64.getDecoder().decode(image)));
}
});
HBox hbox = Containers.hbox(Containers.vbox(prompt, button),
listView);
HBox.setHgrow(listView, Priority.ALWAYS);
Scene scene = new Scene(hbox);
primaryStage.setScene(scene);
primaryStage.show();
}
private Txt2ImgResponse textToImage(String prompt) {
return Unirest.post("http://127.0.0.1:7860/sdapi/v1/txt2img")
.body(new Txt2Img(prompt, 5))
.contentType("application/json")
.asObject(Txt2ImgResponse.class)
.getBody();
}
}
static class InMemoryImageHandle implements ImageHandle {
private final byte[] data;
public InMemoryImageHandle(byte[] data) {
this.data = data;
}
@Override
public byte[] getImageData() {
return data;
}
@Override
public String getKey() {
return null;
}
@Override
public boolean isFastSource() {
return true;
}
}
record Txt2Img(String prompt, int steps) {}
record Txt2ImgResponse(List<String> images) {}
}