Setting ChoiceBox item too slow in javafx - java

i have a problem with setting item in ChoiceBox, so basicly i must load data from a database i do it in another thread :
final Service<ObservableList<Country>> countryService = new Service<ObservableList<Country>>() {
#Override
protected Task<ObservableList<Country>> createTask() {
return new Task<ObservableList<Country>>() {
#Override
protected ObservableList<Country> call() throws Exception {
Dao<Country, Integer> countriesDao = null;
List<Country> result = null;
try {
countriesDao = DaoManager.createDao(Connection.getNewInstance(), Country.class);
System.out.println("getting data");
result = countriesDao.queryForAll();
System.out.println("got data");
} catch (SQLException ex) {
Logger.getLogger(ListClientsController.class.getName()).log(Level.SEVERE, null, ex);
}
return FXCollections.observableArrayList(result);
}
};
}
};
countryService.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
// taking a lot of time here like 4-5 second and freeze
// the gui(normal because it executed in Javafx Application Thread
// but why its take so much time??
cbSearchCountry.setItems(countryService.getValue());
}
});
countryService.start();
Normally database access should take a time longer that setting a list to a ChoiceBox, but no here fetching 150 record from my database is instantaneous but settings observable list to my ChoiceBox take about 5 seconds why?
because i have too much Node in my current Scene??

Use ChoiceBox only if you have < 10 items, else use ComboBox

Related

Dynamical update a textfield on live events

i am at the moment developing a Softphone with javafx. and i kind of a have problem capturing incoming call to a textfield. an example of my code is here.
an incoming call is with Joptionpane successful bt i had like to have the value appear in call textfield just like telephone.
Thank you.
public void telephoneNumbs(String numbers) {
String replace = numbers.replace("sip:", "").trim().replace(".", ""); // Incoming Call Numbers from Sip UA
if (!replace.isEmpty()) {
List<TelephoneObj> telephons;
telTextField.setText(null); //init it with null
costumDao = new CostumersDao(); // costumers DB
telephons = costumDao.getOrCompareTelfone(numbers);
for (TelephoneObj tmp : telephons) {
System.out.println("Test: " + tmp.getTelephoneNums); // am getting exactle what i need here from my Database
//or
JOptionPane.showMessageDialog(null,"incoming:"+ tmp.getTelephoneNums); // it show it during incoming calls
//here is the problem. it wouldnt show the Value on the Textfield
telTextField.setText(tmp.getTelephoneNums); //try to push that Value(Telephone number) to show in JFXTextfield/it cold be any other Textfields
}
}
Sooo much happy today it went well with after 2days of thinking how to solve this miserable life of not taking time to think.
I finally got the answer by using Task to solve the problem.
Task<Void> task = new Task<Void>() {
{
updateMessage("");
}
#Override
public Void call() throws Exception {
while (true) {
updateMessage(callee);
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
break;
}
}
return null;
}
};
//neuLabel.textProperty().bind(task.messageProperty());
kdAddrTel.textProperty().bind(task.messageProperty());
Thread th = new Thread(task);
th.setDaemon(true);
th.start();

How to stop recursive SwingWorker method? JavaFX

I'm using a recursive method which implements the use of the SwingWorker class to do a research in one folder and all its subfolders - in the local hard drive.
Basically works fine but I'm stuck when I want to stop the SwingWorker method: when the user change the 'source folder' (I'm using a JTree - JAVAFX - to show all the folders in the local hard drive), I want to stop the current 'SwingWorker research' in that folder and start a new one, with the newest 'source path' results choosed from the user.
All the results of the research are stored in a private ObservableList - and updated everytime in the done() method, just by filling one TableView - JavaFX: so, when the user change the 'source path' I have to clean the results of the previous research.
Start method:
private static ObservableList<msg> data = FXCollections.observableArrayList();
private static SwingWorker<Void, Void> worker;
private static String currentFolder;
#Override
public void start(Stage primaryStage) throws Exception {
// TODO Auto-generated method stub
stage = primaryStage;
primaryStage.setScene(new Scene(createContent()));
styleControls();
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setMaximized(true);
primaryStage.setFullScreen(false);
primaryStage.show();
msgp = new MsgParser();
}
createContent() method- recursive function its called here:
public Parent createContent() {
tree.getSelectionModel().selectedItemProperty().addListener( new ChangeListener<Object>() {
#Override
public void changed(ObservableValue observable, Object oldValue,
Object newValue) {
TreeItem<File> selectedItem = (TreeItem<File>) newValue;
currentFolder = selectedItem.getValue().getAbsolutePath();
// I want to stop here the previous SwingWorker call : the tree
// ChangeListener event is called when the user change the
// source folder of the research, by selecting one TreeItem on it.
if(worker!= null)
worker.cancel(true);
//Here I clean previous results
data.clear();
TV.setItems(data);
//And I call again the method with the new source Folder
ListMail(new File(currentFolder));
}
});
}
ListMail() method: [recursive SwingWorker]
private void ListMail(File dir) {
worker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
File[] directoryListing = dir.listFiles();
if (directoryListing != null) {
for (File child : directoryListing) {
if(!worker.isCancelled()) {
if(child != null){
if(!child.isDirectory()) {
if(child.getAbsolutePath().substring(child.getAbsolutePath().lastIndexOf('.')+1).equals("msg")) {
Message message = msgp.parseMsg(child.getPath());
String percorsoMail = child.getAbsolutePath().toUpperCase();
if(message != null) {
String fromEmail = message.getFromEmail();
String fromName = message.getFromName();
String subject = message.getSubject();
String received = message.getDate().toString();
String name;
if(fromEmail != null)
name = fromName + "(" + fromEmail + ")";
else name = fromName;
msg Message = new msg(name, subject, received);
if(!data.contains(Message))
data.add(Message);
//I use the Platform.runLater to
// take count of the number of results found
//It updates the GUI - works fine
Platform.runLater(new Runnable() {
#Override public void run() {
if(data != null && data.size() > 0)
setStatusLabel(data.size());
else
setStatusLabel(0);
}
});
}
}
} else {
/**
* Recursive call here : I do the research
* for the subfolders
*/
ListMail(child);
}
} else {
}
}
}
}
return null;
}
// Update GUI Here
protected void done() {
// I refresh here the TableView: works fine on-the-fly added results
TableView.setItems(data);
TableView.refresh();
}
};
//This doesn't do anything
if(!worker.isCancelled())
worker.execute();
}
Basically, the issue is that the SwingWorker thread never stop, I'm thinking because of the recursive calls which creates new pid process at every run or something ?
Also by using a dedicated external button, which I prefer to avoid, gives no results:
refreshBtn.setOnAction(e -> {
//Handle clicks on refreshBtn button
worker.cancel(true);
});
After I click on TreeItem to change source-folder, it just delete all the ObservableList elements created at that moment, but the previous research don't stop.
Everything works fine instead if I wait the research its finished - but this can works only when I'm in a deep-level folder, while I can't obviously wait when the research start with the "C:\" folder.
Ok so that's here how I managed this by using javafx.concurrent.
Just to point my experience with this, it seems using a recursive background Task for potentially long computations, such as scanning the Whole local drive like in my example, it's very memory consuming - also because I stored some results of this background computation in static local variables to access them faster: the result was a data-structure (ObservableList) with over 5000+ instances of a custom class to represent that specific data computed and then the OutOfMemoryError message or the background thread just going like in 'stand-by' without any advice after running for long time (waiting for garbage collection?).
Anyway here's the code that sum up how I solved: the threads are correctly closed. By the way, sometimes, there's a little 'GUI delay' due to cleaning the GUI on the isCancelled() method check: the GUI swing between clear/not clear, because in my opinion it keeps get filled by the results of the previous tasks in the recursion.
private static BackgroundTask backgroundTask;
private static Thread thread;
tree.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Object>() {
#Override
public void changed(final ObservableValue observable, final Object oldValue, final Object newValue) {
//I close previous running background tasks if there's any
if (backgroundTask != null) {
while (backgroundTask.isRunning()) {
backgroundTask.cancel(true);
// reset GUI nodes here used to show results of the previous thread
}
}
backgroundTask = new BackGoundTask();
thread= new Thread(backgroundTask);
thread.setDaemon(true);
thread.start();
//This will be called only when latest recursion is finished, not at every run
backgroundTask.setOnSucceeded(e -> {});
}
});
BackgroundTask class:
public static class BackgroundTask extends Task<Object> {
// .. variables used by the task here
//constructor: initialize variables at every run of the Task
public BackgroundTask() {
}
#Override
protected Object call() throws Exception {
if (!isCancelled()) {
// ... Do all background work here
Platform.runLater(new Runnable() {
#Override
public void run() {
// GUI progress can goes here
}
});
//recursion here
if(something) {
//...
} else {
call();
}
} else {
//user want to cancel task: clean GUI nodes
}
return null;
}
}

Stop Swingworkers correctly

I want to improve my gui-lag-free "live search" of a database with a JTextField.
Imagine you got a large database with entries like 0001, 0002, ... up to a million (just an example; entries could also contain strings). Every time when I press a key I start a Swingworker searching for the current content of a jTextfield (within an added DocumentListener), because I want the search to start immediately after every press of a key.
Lets assume I'd like to search for "123" and enter that into the JTextField...
The first swingworker starts immediately when I enter "1" and searches for all entries which contain a "1" and in it's done()-method sets the model of a jTable:
public static class Xrefreshen_speed_up extends SwingWorker<Void, Void> {
public JXTable JXTabelle;
public String sql_anweisung;
public ResultSet mySQLresultset;
public String get_sql() {
return sql_anweisung;
}
public Xrefreshen_speed_up(JXTable jXT, String str) {
this.JXTabelle = jXT;
this.sql_anweisung = str;
}
#Override
public Void doInBackground() {
try {
Thread.sleep(100);
System.out.println("Following query is now executed: " + sql_anweisung);
Thread.sleep(100);
mySQLresultset = Operationen_MySQL.getInstance().ResultSet_aus_Anweisung(sql_anweisung);
Thread.sleep(100);
} catch (Exception e) {
if (!e.getMessage().contains("sleep")) {
JOptionPane.showMessageDialog(null, "jXTable refresh: Could not refresh table:\n" + e.getMessage());
}
}
return null;
}
#Override
public void done() {
try {
if (!isCancelled()) {
Thread.sleep(100);
if (!isCancelled()) {
TableModel resultSetToTableModel = DbUtils.resultSetToTableModel(this.mySQLresultset);
if (!isCancelled()) {
JXTabelle.setModel(resultSetToTableModel);
JXTabelle.packAll();
}
//Boolean bool = get();
} else {
System.out.println("Another Swingworker interrupted execution.");
}
} else {
System.out.println("Tabellenfüllung von anderer Tabellenfüllung unterbrochen");
}
} catch (Exception e) {
}
}
}
But because I entered "123" there are 2 more Swingworkers: One searching for all entries containing "12" and one searching for "123". Obviously searching for "1" will give more results and therefore that swingworker will take longer than the swingworker searching for "12" or "123"... So the last swingworker ("123") will finish first and will set the TableModel of my JTable... but after that the "12"-Swingworker will overwrite the model...and then the "1"-Swingworker... you get it...
So I tried following in the documentlistener of the jTextfield and put multiple Thread.sleep into the Swingworker (see code above):
if(swingworker != null){
swingworker.cancel(true);
}
swingworker = new swingworker_class(null);
swingworker.execute();
But somehow the "1"- and "12"-Swingworkers still won't stop and both overwrite the TableModel... So I end up with the results of the "1"-swingworker...

Add new elements in a Table View JavaFx dynamically

Guys this is the code I use to add elements in a Table View when the button theButton is pressed.
The Table View is filled with elements taken from the web, so to fill the whole Table View takes about 30 seconds...
I would like to add all the data dynamically, not wanting for all the data to put into the Table will be ready. So now it happens that when the button is pressed, it takes 30 seconds or so to display the results. This is my code, is there something wrong with it? Thank you.
theButton.setOnAction(new EventHandler()
{
#Override
public void handle(Event event)
{
try
{
ObservableList<Elements> toShow = FXCollections.observableArrayList();
ArrayList<String> strings = takeSomeStrings();
for (int i = 0; i < strings.size(); i++)
{
toShow.add(new Elements(takeInfoFromTheWeb(strings.get(i))));
myTableView.setItems(toShow);
}
} catch (FileNotFoundException e)
{
e.printStackTrace();
}
}
}
}
Use an executor that fetches the data from the Web in background. Once fetched, use Platform.runLater() to modify the TableView on the JavaFX application thread.
import java.util.concurrent.Executors;
private final Executor executor = Executors.newSingleThreadExecutor();
theButton.setOnAction(event ->
{
try
{
ObservableList<Elements> toShow = FXCollections.observableArrayList();
myTableView.setItems(toShow);
ArrayList<String> strings = takeSomeStrings();
for (String s: strings)
{
executor.execute(() -> {
Elements el = new Elements(takeInfoFromTheWeb(s));
Platform.runLater(() -> toShow.add(el));
});
}
} catch (FileNotFoundException e)
{
e.printStackTrace();
}
});
If you want to fetch the data in parallel, just replace the executor, e.g.
private final Executor executor = Executors.newFixedThreadPool(4);
And you will be downloading up to 4 items in parallel.

GXT-3 issue to load data for my Chart

My problem is annoying. My server side is generating 12 random numbers (double here).
My Client side received the correct data but nothing is displayed in my Chart. That worked fine with hardcoded data in the store but not with a REST call.
The transfer between my server and my client is that :
[{"key":"key0","value":0.47222548599297787},{"key":"key1","value":0.6009173797369691},{"key":"key2","value":0.13880104282435624},{"key":"key3","value":0.01804674319345545},{"key":"key4","value":0.5547733564202956},{"key":"key5","value":0.8229999661308851},{"key":"key6","value":0.8959346004391032},{"key":"key7","value":0.6848052288628435},{"key":"key8","value":0.10222856671111813},{"key":"key9","value":0.6931371931409103},{"key":"key10","value":0.2994297934549003},{"key":"key11","value":0.47566752196381334}]
Here my simple class used for my test. I am a newbie with GXT 3
public void onModuleLoad() {
final ListStore<JSOModel> store;
final ContentPanel panel = new FramedPanel();
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, "/ws/DocumentService/v1/test");
builder.setHeader("Accept", "application/json");
HttpProxy proxy = new HttpProxy(builder);
final Loader<ListLoadConfig, ListLoadResult<JSOModel>> loader = new ListLoader<ListLoadConfig, ListLoadResult<JSOModel>>(proxy, new DataReader<ListLoadResult<JSOModel>, String>() {
#Override
public ListLoadResult<JSOModel> read(Object loadConfig, String data) {
List<JSOModel> jsoModels = new ArrayList<JSOModel>();
JsArray<JSOModel> jsoModelJsArray = JSOModel.arrayFromJson(data);
if(jsoModelJsArray != null) {
for(int i = 0; i < jsoModelJsArray.length(); i++) {
jsoModels.add(jsoModelJsArray.get(i));
}
}
return new ListLoadResultBean<JSOModel>(jsoModels);
}
});
store = new ListStore<JSOModel>(new ModelKeyProvider<JSOModel>() {
#Override
public String getKey(JSOModel item) {
return item.get("key");
}
});
loader.addLoadHandler(new LoadResultListStoreBinding<ListLoadConfig, JSOModel, ListLoadResult<JSOModel>>(store) {
#Override
public void onLoad(LoadEvent<ListLoadConfig, ListLoadResult<JSOModel>> event) {
ListLoadResult<JSOModel> loaded = event.getLoadResult();
if(loaded.getData() == null) {
store.replaceAll(new ArrayList<JSOModel>());
} else {
store.replaceAll(loaded.getData());
}
}
});
Chart<JSOModel> chart = new Chart<JSOModel>();
chart.setStore(store);
chart.setShadowChart(true);
NumericAxis<JSOModel> axis = new NumericAxis<JSOModel>();
axis.setPosition(Chart.Position.LEFT);
axis.addField(new ValueProvider<JSOModel, Number>() {
#Override
public Number getValue(JSOModel JSOModel) {
return JSOModel.getNumber("value");
}
#Override
public void setValue(JSOModel JSOModel, Number number) {
}
#Override
public String getPath() {
return "key";
}
});
axis.setTitleConfig(new TextSprite("Number of hits"));
axis.setWidth(50);
axis.setMinimum(0);
axis.setMaximum(100);
chart.addAxis(axis);
PathSprite odd = new PathSprite();
odd.setOpacity(1);
odd.setFill(new Color("#dff"));
odd.setStroke(new Color("#aaa"));
odd.setStrokeWidth(0.5);
axis.setGridOddConfig(odd);
CategoryAxis<JSOModel, String> horizontalAxis = new CategoryAxis<JSOModel, String>();
horizontalAxis.setPosition(Chart.Position.BOTTOM);
horizontalAxis.setField(new ValueProvider<JSOModel, String>() {
#Override
public String getValue(JSOModel JSOModel) {
return JSOModel.get("key");
}
#Override
public void setValue(JSOModel JSOModel, String s) {
}
#Override
public String getPath() {
return "key";
}
});
horizontalAxis.setTitleConfig(new TextSprite("month of year"));
chart.addAxis(horizontalAxis);
LineSeries<JSOModel> column = new LineSeries<JSOModel>();
column.setYAxisPosition(Chart.Position.LEFT);
column.setStroke(new RGB(148,174,10));
column.setHighlighting(true);
chart.addSeries(column);
axis.addField(column.getYField());
chart.addSeries(column);
chart.setHeight(100);
chart.setWidth(100);
Button b = new Button("ha");
b.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent clickEvent) {
loader.load();
}
});
RootPanel.get().add(b);
panel.setCollapsible(true);
panel.setHeadingText("Column Chart");
panel.setPixelSize(620, 500);
panel.setBodyBorder(true);
VerticalLayoutContainer layout = new VerticalLayoutContainer();
panel.add(layout);
chart.setLayoutData(new VerticalLayoutContainer.VerticalLayoutData(1,1));
layout.add(chart);
chart.setBackground(new Color("#dff"));
RootPanel.get().add(panel);
There are two ways to wire the chart into a store. One is to simply specify that the chart is using a store via setStore, as you have done:
chart.setStore(store);
When you do this, you must also inform the chart when it must redraw everything - you must call:
chart.redrawChart();
This call must be made shortly after the load is completed - consider doing it at the end of onLoad.
Why is this required? In some cases, developers want to make many changes to the store, one at a time, and if the chart automatically updated after each change, that would spawn many slow changes to the data model, and could end up looking strange. In a case like this, you would only call redrawChart() after all changes were complete.
There is another option however - instead of calling setStore, you can call bindStore, and ask the Chart to automatically update whenever any change occurs to the chart:
chart.bindStore(store);
In your case, this is likely the correct answer.

Categories

Resources