I have a little problem.I have an Stage with a table view . Im counting the rows with this method:
public void count() {
int number;
number= tableview.getItems().size();
lblcount.setText(String.valueOf(number));
}
I have a second stage with the same tableview ,but in this stage i can delete rows. I need to refresh the first tableview or the count, in each change what im doing in the second tableview.
BUt if i call the method from the second stage nothing happens.
Im doing this in the second Stage:
Class1 C1 = new Class1();
C1.count();
I need to execute the method in the first Stage and update or refresh the count of the tableview from the other Stage.
Thanks in advance.. :D
Related
Problem:
I am working on a client server desktop application using JavaFx, everything works fine except that I have found that when I open a new stage clicking a button on the Home Page, the new Stage loads data only the first time I open it. The initialize method of the new stage sends a request to server and receives back an object whose fields are loaded on the new stage. None exception is thrown, the server always send the object correctly (tested by printing its vales on console).
My suspicion is that when I click the button to open a new stage, it creates a new instance of the FXML Loader each time. What I don't get is dued to the fact that the execution cycle is always the same both the first time I open the stage and the following ones.
I finally tried to insert a button in the second window which, when clicked, loads the object on the window, and this works correctly also when I open the window multiple times, but obviously I don't like this solution, which would force the user to click the button to retreive data.
Here is the HomePage Controller method that allows to open the new stage:
#FXML
void showSecondView(MouseEvent event) throws IOException {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("/View_FXML/generaRichiesta.fxml"));
Pane secondPageLayout = loader.load();
SecondController secCon = loader.getController();
stage = new Stage();
stage.setScene(new Scene(secondPageLayout));
stage.setTitle("Second Stage");
stage.show();
}
Here is the Initialize method for the Second Stage
public void initialize(URL url, ResourceBundle resourceBundle) {
//sending request to server and calling the following function to update the
//view withe the received object
Platform.runLater(
() -> {
//setting the view node "textField" with the object received from server
//if I print this value on console I always get the object correctly but
//it is loaded only the first time. When i call this function from a button it always
//works but I should do it manually
textField.setText().receivedObject.toString();
}
);
}
I really don't get what the problem could be, I think it may be because when I create every time and FXMLLoader object.
Any suggestions?
Thanks
I found the error, basically I'm using the Observable Pattern, every time I opened a controller, I associated the controller object in a hashmap and didn't accept other insertion of objects of the same class. Also each time I closed the second stage, I omitted to remove to remove the first instance of the controllere from the map, so the Observable Pattern sent notifications of changes only and always to the first instance of the controller created and not to the subsequent ones that were created by reopening the window
I have TableColumn with CheckBox. I have to do this listener and I wonder why the listener doubles after every click.
selectedColumn.setCellFactory(column -> new CheckBoxTableCell<>());
selectedColumn.setCellValueFactory(cellData -> {
Dir dir = cellData.getValue();
BooleanProperty property = dir.isSelectedProperty();
property.addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) ->{
System.out.println(newValue);
});
return property;
});
First click in checkbox i row return:
True
True
Second unselect return:
False
False
False
False
Thrid select return:
True
True
True
True
True
True
why ? :)
This is happening because controls like TableView (and ListView etc) virtualizes its contents. From TableView Javadoc:
The TableView control is designed to visualize an unlimited number of
rows of data, broken out into columns.
The TableView may hold a large number of items. However, on the screen, you could probably see 10-30 rows (each corresponding to a single item) at any point in time.
Without virtualization, if you have 1 million items, it would create 1 million TableRow objects (we haven't talked about TableCell), with each TableRow maintaining all the states/values. This takes a lot of memory and processing power.
On the other hand, with virtualization, if your View can only display 10 rows, TableView will only create, for example, 12 TableRow instances. As you scroll through the list, some of the items disappear from your sight. These TableRow instances are immediately reused for items that enters your sight.
This is why setCellFactory() and setCellValueFactory() methods are of Callback type. Each time a row was reused, it will call this callback object to update the TableCell. This is why your listener is being added repeatedly, causing this.
Depending on what you need, it may be possible to add a ListChangedListener to the list of items.
ObservableList<Dir> list = FXCollections.observableArrayList(item -> new javafx.beans.Observable[] {item.isSelectedProperty()});
list.addAll(DirList.getDirList());
dirList.setItems(list);
list.addListener(new ListChangeListener<Dir>() {
#Override public void onChanged(javafx.collections.ListChangeListener.Change<? extends Dir> c) {
while (c.next()) {
if (c.wasUpdated()) {
// Do something
}
}
}
});
I see you have a model named Dir with a BooleanProperty named selected
You can simply let the cellValueFactory just:
selectedColumn.setCellValueFactory(cellData -> cellData.getValue().isSelectedProperty());
This would update the model's property if you check/un-check the CheckBox in the table cell.
If you use this selected/deselected state of the checkbox, you can use the model, and add the listener there, you will get the same result. Then the listener will be added just once.
You should have instances of Dir created then you can do the following for ex. in initialize:
dir1.selectedProperty().addListener((observable, oldValue, newValue) -> {
dir2.selectedProperty().set(oldValue);
});
Or anything you want, then you are sure that the listener is added just once.
As #Jai mentioned, the cell data is used many time and every time the CallBack is called, the listener is added to the property, so don't use it inside that method if you want to avoid adding the listener many times to the property.
I am facing problem in updating values in my table view.
I have a for loop which updates all my rows values. I am using StringProperty as my model class field types. When I update a single row it works fine. But my requirement is I need to stop for 1000 millisec in each row after updation. I tried using Thread.sleep(1000) in my for loop which is not helping :(
Don't try to manipulate your Thread, it will be too complicate.
Simply use a TimeLine like this :
timer = new Timeline(new KeyFrame(Duration.millis(1000), new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
//Pause your timer in order to update your row
timer.pause();
//Do your update (increment your rowIndex inside)
updateRowMethod(rowIndex);
//Then launch the Timer and exit
timer.play();
}
}));
I use the pause method because your said you wanted to wait 1000 milliseconds after the edition of a line. If you just want to edit a line every second, skip the pause/play part.
I am looking for a way to get the selected cell of a TableView control. Note that I don't just want the cell value, I want an actual TableCell object. One would expect that the method:
tableView.getSelectionModel().getSelectedCells().get(0)
does just that, but it returns a TablePosition object, which gives you row and column information, but I don't see a way to get TableCell object from that.
The reason I need this is because I want to respond to a key press, but attaching an event filter to TableCell does not work (probably because it is not editable). So I attach it to TableView, but then I need to get the currently selected cell.
EDIT: For future readers: DO NOT mess with TableCell objects, except in cell factory. Use the TableView the way designers intended, or you will be in lot of trouble. If you need data from multiple sources in single table, it is better to make a new class that aggregates all the data and use that as a TableView source.
I just posted an answer that uses this code to edit a Cell. I don't think you can get a reference to the actual table cell as that's internal to the table view.
tp = tv.getFocusModel().getFocusedCell();
tv.edit(tp.getRow(), tp.getTableColumn());
Your method also returns a TablePosition so you can use that as well.
Here's the link https://stackoverflow.com/a/21988562/2855515
This will probably get downvoted because the OP asked about returning the cell itself, rather than what I'll describe, but a Google search led me here for my issue.
I personally ran into issues trying to retrieve data from an individual cell.
java.is.for.desktop offered buggy code related to this matter, that throws an ArrayIndexOutOfBoundsException, but is on the right track. My goal is to offer a better example of that using a lambda.
To access data from a single TableCell:
tableView.getFocusModel().focusedCellProperty().addListener((ObservableValue<? extends TablePosition> observable, TablePosition oldPos, TablePosition pos) -> {
int row = pos.getRow();
int column = pos.getColumn();
String selectedValue = "";
/* pos.getColumn() can return -1 if the TableView or
* TableColumn instances are null. The JavaDocs state
* this clearly. Failing to check will produce an
* ArrayIndexOutOfBoundsException when underlying data is changed.
*/
if ((pos.getRow() != -1) && (pos.getColumn() != -1))
{
selectedValue = tableView.getItems()
.get(row)
.get(column);
if ((selectedValue != null) && (!selectedValue.isEmpty()))
{
// handling if contains data
}
else
{
// handling if doesn't contain data
}
}
});
Edit:
I meant to say ArrayIndexOutOfBoundsException, rather than NullPointerException, I updated this answer to reflect that. I also cleaned up spelling and grammar.
You want to respond to key press? Better don't.
Instead, you could register a listener for focusing of table cells, which would work with arrow keys and mouse clicks on table cells (and even with touch events, oh my, the future is already there).
table.getFocusModel().focusedCellProperty().addListener(
new ChangeListener<TablePosition>() {
#Override
public void changed(ObservableValue<? extends TablePosition> observable,
TablePosition oldPos, TablePosition pos) {
int row = pos.getRow();
int column = pos.getColumn();
String selectedValue = "";
if (table.getItems().size() > row
&& table.getItems().get(row).size() > column) {
selectedValue = table.getItems().get(row).get(column);
}
label.setText(selectedValue);
}
});
In this example, I am using a "classic" TableView with List<String> as column model. (So, your data type could be different than String.) And, of course, that label is just an example from my code.
I have a Qt Form that contains 2 combo box messages. The second combobox message depends on the first combo box message. I mean that the dates from the second combobox message depends on the element that I select in the first combobox.
In this moment I have different dates in the first combobox. But the second combobox is not working. I need to creare a connect method or what?
Thx! APpreciate!
Could someone give me a short example?
It's fairly simple. A combobox emits the currentIndexChanged signal that also tells you the new index. Write a method that accepts an integer and changes the second combobox according to the integer (which is the index of the selection in combobox 1).
Here are some code sniplets from a working example.
Method declaration in your window/whatever class header:
public slots:
void setI1(int index);
Filling combobox 1, connecting the signal, e.g. in the constructor:
i1Box->addItem("Neutral", 0);
i1Box->addItem("2,856 K (Illuminant A, light bulb)", 2856);
// ...
connect(i1Box, SIGNAL(currentIndexChanged(int)),
this, SLOT(setI1(int)));
Implementation of the method:
void ViewerWindow::setI1(int index) {
// either use index directly, or, as in this case we have items holding an int:
int i1 = i1Box->itemData(index).value<int>();
// use the value to change second combobox here
}
If it does not work as expected, it is always helpful to print some debug output inside the method that should be called to see where it goes wrong in the chain.
Reference: http://doc.qt.nokia.com/latest/signalsandslots.html