Is it possible to render in one progressbar the progress of 2 nested tasks?
I mean, if I have to read lines from several files in several directories. I have this snippet code:
EDIT:
final int steps = directories.size();
Task<Integer> task2 = new Task<Integer>() {
/**
*/
#Override
protected Integer call() throws Exception {
int len = files.length;
updateProgress(0, len);
for(int i = 0; i < files.length; i++) {
final String filename = files[i].getName();
final String file = files[i].getPath();
try {
BufferedReader br = new BufferedReader(new FileReader(file));
}
String currentLine = null;
while ((currentLine = br.readLine()) != null) {
readlines(currentLine, filename);
}
} catch (IOException xs) {
xs.getMessage();
}
System.out.println("******* Working on: " + file + " ******************************");
updateProgress(i + 1, files.length);
}
System.out.println("done: " + directoryName);
return len;
}
};
task2.setOnRunning(r -> {
DoubleBinding totalProgress = null;
for(int i = 0; i < steps; i++) {
DoubleBinding increment = task2.progressProperty().divide(steps);
if (totalProgress == null) {
totalProgress = increment;
} else {
totalProgress = totalProgress.add(increment);
}
}
progressBar.progressProperty().bind(totalProgress);
status.textProperty().bind(task2.stateProperty().asString());
statusProcess.textProperty().bind(Bindings.when(totalProgress.isEqualTo(-1))
.then("0.00")
.otherwise(task2.progressProperty().multiply(100.00).asString("%.02f %%")));
console.textProperty().bind(task2.messageProperty());
});
new Thread(task2).start();
I am enable to access progress bar but as long as I have directories, the progress bar will restart to 0 and will check for files into the next directory and then populate the progress bar until 1 and then restart to 0 if there is another directory.
How can I do if I want to display these 2 tasks in one as I don't want to restart from 0 when the next directory is reading. If I have for example 4 directories, I expect to see a moving the progressbar until its quarter and so on.
Is anybody as an idea what I am doing wrong?
You can do something like
progressBar.progressProperty().bind(Bindings.createDoubleBinding(
() -> Math.max(0, task1.getProgress()) / 2 + Math.max(0, task2.getProgress()) /2 ,
task1.progressProperty(), task2.progressProperty());
If you can easily estimate or compute the relative times each task will take, you can weight the progress of each task accordingly, instead of simply averaging them as in the code above.
Related
I use ListChangeListener to listen to changes in Tab Pane.
private final TabPane tabBar = new TabPane();
...
tabBar.getTabs().addListener(this::tabsChanged);
I'm trying to listen to tab move events with the following code:
private void tabsChanged(ListChangeListener.Change<? extends Tab> change) {
while (change.next()) {
if (change.wasPermutated()) {
for (int i = change.getFrom(); i < change.getTo(); i++) {
System.out.println(i + " -> " + change.getPermutation(i));
}
}
}
}
As JavaFX documentation says:
In order to get the new position of an element, you must call:
change.getPermutation(oldIndex). Returns: the new index of the same
element.
But in my case change.getPermutation(i) always returns just i.
For example, I have 4 tabs.
Their indexes are: 0, 1, 2, 3.
Then I move the 4th tab to the first position.
I expect the following output:
0 -> 1
1 -> 2
2 -> 3
3 -> 0
But I get:
0 -> 0
1 -> 1
2 -> 2
3 -> 3
How can I make it work as I need?
As already noted in the comments: the behavior you observe is a bug just reported as JDK-8278062 - the doc and your expectation based on the doc is correct, the notification (implemented in the internal class TabObservableList) is wrong.
Normally, if we want to find the newIndex, a listChangeListener would do something like:
for (int oldIndex = c.getFrom(); oldIndex < c.getTo(); ++oldIndex) {
int newIndex = c.getPermutation(oldIndex);
...
}
To work around the issue, we could manually keep a copy of the tabs, lookup the tab at the old index and find its new index in the re-ordered tabs:
for (int oldIndex = c.getFrom(); oldIndex < c.getTo(); ++oldIndex) {
Tab tab = copy.get(oldIndex);
int newIndex = c.getList().indexOf(tab);
...
}
// update the copy
Or we could have some fun and implement a TransformationList around the original tabs that does the work for us :) It jumps in when it detects a permutation and fires the correct notification. Note that the only internal class used below is SourceChangeAdapter, we either need to relax encapsulation or c&p its content (it is doing nothing but pass on notifications on behalf of the wrapper)
public class TabObservableListWrapper extends TransformationList<Tab, Tab> {
// copy of source used to build the correct permutation
private ObservableList<Tab> copy = FXCollections.observableArrayList();
public TabObservableListWrapper(ObservableList<Tab> source) {
super(source);
updateCopy();
}
#Override
protected void sourceChanged(Change<? extends Tab> c) {
// TBD: cope with a change that has
// - a mixture of permutation and other subchanges
// - multiple subchanges of type permutation
boolean isPermutation = false;
// check if the change is a permutation
while (c.next()) {
if (c.wasPermutated()) {
isPermutation = true;
break;
}
}
c.reset();
if (isPermutation) {
beginChange();
updatePermutation(c);
endChange();
} else {
// assuming other change type notifications are correct, just delegate
fireChange(new SourceAdapterChange<>(this, c));
}
// keep copy sync'ed to source
updateCopy();
}
/**
* Converts the incorrect permutation notification from source
* into a correct one and let super fire the appropriate change.
*
* Note: this method must be called inside a begin/endChange block.
* #param c a change with a single subChange of type wasPermutated
*/
private void updatePermutation(Change<? extends Tab> c) {
c.next();
int from = c.getFrom();
int to = c.getTo();
int permSize = to - from;
int[] perm = new int[permSize];
// fill the perm
for(int i = 0; i < permSize; i++) {
int oldIndex = from + i;
Tab tab = copy.get(oldIndex);
perm[i] = c.getList().indexOf(tab);
}
nextPermutation(from, to, perm);
}
// keep copy sync'ed
private void updateCopy() {
copy.setAll(getSource());
}
// implement public methods by delegating 1:1 to source
#Override
public int getSourceIndex(int index) {
return index;
}
#Override
public int getViewIndex(int index) {
return index;
}
#Override
public Tab get(int index) {
return getSource().get(index);
}
#Override
public int size() {
return getSource().size();
}
}
To use, wrap it around a tabPane's tab list and listen to the wrapper instead of directly to original list, something like:
TabObservableListWrapper wrapper = new TabObservableListWrapper(tabPane.getTabs());
wrapper.addListener((ListChangeListener<Tab>)change -> {
while (change.next()) {
if (change.wasPermutated()) {
System.out.println("from wrapper:");
for (int oldIndex = change.getFrom(); oldIndex < change.getTo(); oldIndex++) {
System.out.println(oldIndex + " -> " + change.getPermutation(oldIndex));
}
}
}
});
I am new to RocksDB abd trying to create a SST file in Java for bulk loading. Eventual usecase is to create this in Apache Spark.
I am using rocksdbjni 6.3.6 in Ubuntu 18.04.03
I am keep getting this error,
org.rocksdb.RocksDBException: Keys must be added in order
at org.rocksdb.SstFileWriter.put(Native Method)
at org.rocksdb.SstFileWriter.put(SstFileWriter.java:104)
at CreateSSTFile.main(CreateSSTFile.java:34)
The sample code is
public static void main(String[] args) throws RocksDBException {
RocksDB.loadLibrary();
final Random random = new Random();
final EnvOptions envOptions = new EnvOptions();
final StringAppendOperator stringAppendOperator = new StringAppendOperator();
Options options1 = new Options();
SstFileWriter fw = null;
ComparatorOptions comparatorOptions = new ComparatorOptions();
try {
options1 = options1
.setCreateIfMissing(true)
.setEnv(Env.getDefault())
.setComparator(new BytewiseComparator(comparatorOptions));
fw = new SstFileWriter(envOptions, options1);
fw.open("/tmp/db/sst_upload_01");
for (int index = 0; index < 1000; index++) {
Slice keySlice = new Slice(("Key" + "_" + index).getBytes());
Slice valueSlice = new Slice(("Value_" + index + "_" + random.nextLong()).getBytes());
fw.put(keySlice, valueSlice);
}
fw.finish();
} catch (RocksDBException ex) {
ex.printStackTrace();
} finally {
stringAppendOperator.close();
envOptions.close();
options1.close();
if (fw != null) {
fw.close();
}
}
}
If the loop index is less than 10 the file is created successfully and I was able to ingest that into rocks db.
Thanks in advance.
I think I found the problem with the code.
The keys must be in order for the SST. The way I do the looping and using String lexicographical comparison for ordering, produces incorrect ordering. Like comparing "10" and "9" would break the order. Instead of that, if I sort all the keys before inserting into SST file it works.
Map<String, String> data = new HashMap<>();
for (int index = 0; index < 1000; index++) {
data.put("Key-" + random.nextLong(), "Value-" + random.nextDouble());
}
List<String> keys = new ArrayList<String>(data.keySet());
Collections.sort(keys);
for (String key : keys) {
Slice keySlice = new Slice(key);
Slice valueSlice = new Slice(data.get(key));
fw.put(keySlice, valueSlice);
}
When I tried with integer keys I found the issue.
The keys in sstfile should be in increasing order.
so you can start from index=10 it will work.
In fact ,you can create Comparator extends DirectComparator to avoid sorted.
class MyComparator extends DirectComparator {
public MyComparator(ComparatorOptions copt) {
super(copt);
}
#Override
public String name() {
return "MyComparator";
}
#Override
public int compare(DirectSlice a, DirectSlice b) {
// always true
return 1;
}
}
}
then set option
options1.setComparator(new MyComparator(comparatorOptions));
So I'm creating a monopoly game, and I've got two functions which read/write information about the game to/from a database so it can be retrieved between postbacks. I also have a start game function which initialises everything to 0 and then calls the writeToDatabase(). createArrays() just creates a list of players/squares, squares have been removed as they'not relevant.
The problem I'm having is I believe it is only storing some of the values within the loop. When the start game function is called it correctly sets the players positions to 0, and their money to 1500, however for some reason when reading from the database again the 4th players details immediately go back to what they were during the previous session.
private void readFromDatabase()
{
createArrays();
for (int i = 0; i < players.Count(); i++)
{
players[i].SetID(Convert.ToInt32(players_table.GetRow(i)["ID"]));
players[i].SetPosition(Convert.ToInt32(players_table.GetRow(i)["position"]));
players[i].SetMoney(Convert.ToInt32(players_table.GetRow(i)["money"]));
players[i].SetInJail(Convert.ToBoolean(players_table.GetRow(i)["isInJail"]));
players[i].SetIsBankrupt(Convert.ToBoolean(players_table.GetRow(i)["isBankrupt"]));
}
currentPlayerID = Convert.ToInt32(gameinfo_table.GetRow(0)["CurrentPlayerID"]);
currentSquareID = Convert.ToInt32(gameinfo_table.GetRow(0)["CurrentSquareID"]);
freeParkingAmount = Convert.ToInt32(gameinfo_table.GetRow(0)["FreeParkingAmount"]);
currentPlayer = players[currentPlayerID];
CurrentSquare = squares[currentSquareID];
}
private void writeToDatabase()
{
CurrentSquare = squares[currentSquareID];
for (int i = 0; i < players.Count(); i++)
{
SQLDatabase.DatabaseRow prow = players_table.GetRow(players[i].GetID());
prow["ID"] = players[i].GetID().ToString();
prow["position"] = players[i].GetPosition().ToString();
prow["money"] = players[i].GetMoney().ToString();
prow["isInJail"] = players[i].IsInJail().ToString();
prow["isBankrupt"] = players[i].IsBankrupt().ToString();
players_table.Update(prow);
}
SQLDatabase.DatabaseRow girow = gameinfo_table.GetRow(0);
girow["CurrentPlayerID"] = currentPlayerID.ToString();
girow["CurrentSquareID"] = CurrentSquare.GetID().ToString();
girow["FreeParkingAmount"] = freeParkingAmount.ToString();
gameinfo_table.Update(girow);
}
private void startGame()
{
createArrays();
currentPlayerID = 0;
currentPlayer = players[0];
CurrentSquare = squares[0];
writeToDatabase();
}
private void createArrays()
{
for (int i = 0; i < 4; i++)
{
Player player = new Player(i);
players.Add(player);
}
}
I really can't work out why this is happening, as when it creates the player array it loops through 4 times, therefore creating 4 brand new players, and immediately overwriting the details in the database. It just seems odd it works perfectly fine for the first 3 players but the 4ths details don't seem to change. Any help would be greatly appreciated, I'm sure it something simple but I have spent hours on this and haven't got anywhere.
I need to view the segments and handles of the path that defines a SymbolItem. It is a related issue to this one but in reverse (I want the behavior displayed on that jsfiddle).
As per the following example, I can view the bounding box of the SymbolItem, but I cannot select the path itself in order to view its segments/handles. What am I missing?
function onMouseDown(event) {
project.activeLayer.selected = false;
// Check whether there is something on that position already. If there isn't:
// Add a circle centered around the position of the mouse:
if (event.item === null) {
var circle = new Path.Circle(new Point(0, 0), 10);
circle.fillColor = '#' + Math.floor(Math.random() * 16777215).toString(16);
var circleSymbol = new SymbolDefinition(circle);
multiply(circleSymbol, event.point);
}
// If there is an item at that position, select the item.
else {
event.item.selected = true;
}
}
function multiply(item, location) {
for (var i = 0; i < 10; i++) {
var next = item.place(location);
next.position.x = next.position.x + 20 * i;
}
}
Using SymbolDefinition/SymbolItem prevent you from changing properties of each symbol items.
The only thing you can do in this case is select all symbols which share a common definition.
To achieve what you want, you have to use Path directly.
Here is a sketch showing the solution.
function onMouseDown(event) {
project.activeLayer.selected = false;
if (event.item === null) {
var circle = new Path.Circle(new Point(0, 0), 10);
circle.fillColor = Color.random();
// just pass the circle instead of making a symbol definition
multiply(circle, event.point);
}
else {
event.item.selected = true;
}
}
function multiply(item, location) {
for (var i = 0; i < 10; i++) {
// use passed item for first iteration, then use a clone
var next = i === 0 ? item : item.clone();
next.position = location + [20 * i, 0];
}
}
I am a little stuck and need some advice/help.
I have a progress bar:
<mx:ProgressBar id="appProgress" mode="manual" width="300" label="{appProgressMsg}" minimum="0" maximum="100"/>
I have two listener functions, one sets the progress, and one sets the appProgressMsg:
public function incProgress(e:TEvent):void {
var p:uint = Math.floor(e.data.number / e.data.total * 100);
trace("Setting Perc." + p);
appProgress.setProgress(p, 100);
}
public function setApplicationProgressStep(e:TEvent):void {
trace("Setting step:" + e.data);
appProgressMsg = e.data;
}
I want to reuse this progress bar alot. And not necessarily for ProgressEvents, but when going through steps.
For instance, I loop over a bunch of database inserts, and want to undate the progress etc.
Here is a sample:
public function updateDatabase(result:Object):void {
var total:int = 0;
var i:int = 0;
var r:SQLResult;
trace("updateDatabase called.");
for each (var table:XML in this.queries.elements("table")) {
var key:String = table.attribute("name");
if (result[key]) {
send(TEvent.UpdateApplicationProgressStep, "Updating " + key);
i = 1;
total = result[key].length;
for each (var row:Object in result[key]) {
//now, we need to see if we already have this record.
send(TEvent.UpdateApplicationProgress, { number:i, total: total } );
r = this.query("select * from " + key + " where server_id = '" + row.id + "'");
if (r.data == null) {
//there is no entry with this id, make one.
this.query(table.insert, row);
} else {
//it exists, so let's update.
this.update(key, row);
}
i++;
}
}
}
}
Everything works fine.
That is, the listener functions are called and I get trace output like:
updateDatabase called.
Setting step:Updating project
Setting Perc 25
Setting Perc 50
Setting Perc 75
Setting Perc 100
The issue is, only the very last percent and step is shown. that is, when it's all done, the progress bar jumps to 100% and shows the last step label.
Does anyone know why this is?
Thanks in advance for any help,
Jason
The new code, which works awesomely I might add:
public function updateDatabase(result:Object, eindex:int = 0, sindex:int = 0 ):void {
var total:int = 0;
var i:int = 0;
var j:int;
var r:SQLResult;
var table:XML;
var key:String;
var elems:XMLList = this.queries.elements("table");
var startTime:int = getTimer();
var row:Object;
for (i = eindex; i < elems.length(); i++) {
table = elems[i];
key = table.attribute("name");
if (!result[key])
continue;
total = result[key].length;
send(TEvent.UpdateApplicationProgressStep, "Updating " + key);
for (j = sindex; j < result[key].length; j++) {
if (getTimer() - startTime > 100) {
setTimeout(updateDatabase, 100, result, i, j);
send(TEvent.UpdateApplicationProgress, { number:j, total: total } );
return;
}
row = result[key][j];
r = this.query("select * from " + key + " where server_id = '" + row.id + "'");
if (r.data == null) {
//there is no entry with this id, make one.
this.query(table.insert, row,false);
} else {
//it exists, so let's update.
this.update(key, row,false);
}
}
send(TEvent.UpdateApplicationProgress, { number:1, total: 1 } );
}
}
Flash is single threaded. The display will not update until your function returns. For this reason, you should never have any code that runs for longer than about 100ms (1/10th of a second), otherwise the UI (or even the entire browser) will appear to be locked up.
The general solution is to split up your work over multiple frames, here is some pseudo-code:
function doWork(arg1:Obj, arg2:Obj, start:int=0) {
var startTime = getTimer(); // store starting time
for(i=start; i<length; i++) {
if(getTimer() - startTime > 100) { // see if we've been working too long
trace("Current progress: "+(i/length * 100)+"%");
updateProgress( i / length );
setTimeout(doWork, 100, arg1, arg2, i); // schedule more work to be done
return; // stop current loop
}
trace("Working on item "+i);
// processing here
}
trace("All work done");
}
doWork(data1, data2); // start the work
Your pseudo-code works for updating the progress bar however in my case my "work" was copying of files from DVD to the appStorageDirectory which seem to reintroduce the same issue that your work around resolved - the progress bar now does not update
Here is my hack of your solution
function doWork(arg1:int, arg2:int, start:int=0) {
var startTime = getTimer(); // store starting time
for(var i:int=start; i<arg2; i++) {
if(getTimer() - startTime > 100 ) { // see if we've been working too long
trace("Current progress: "+(i/arg2 * 100)+"%");
setTimeout(doWork, 100, i, arg2, i); // schedule more work to be done
return; // stop current loop
}
trace("Working on item "+i);
dispatchEvent(new progressMadeEvent("incrementChange",i,arg2))
var dir:String = copyRes[i].nativePath.toString().split(OSSep).pop()
copyRes[i].copyTo(appStore.resolvePath(dir)) // copies dir from DVD to appStorageDir
}
trace("All work done");
}