I need to edit the contents of chunkLarge, so I am trying to move them into a duplicate GridPane:
chunkLarge2 = new GridPane();
for (Node n : chunkLarge.getChildren()) {
chunkLarge2.add(n, GridPane.getColumnIndex(n), GridPane.getRowIndex(n));
}
This throws a ConcurrentModificationException. I think it's because of GridPane.get...Index(n).
I did a bit of searching online, and found a few things.
One was that I could use an Iterator to cycle through lists, but I am unsure how to apply it here.
Next was that I could try .getChildrenUnmodified() instead of your standard .getChildren(), but this just threw NoSuchElementException instead.
Any ideas?
ConcurrentModificationException is thrown because you're iterating over chunkLarge's children list and deleting elements from it at the same time.
Deletion happens when you try to add a child node to chunkLarge2 - a javafx node can have only one parent, so the child node n is removed from chunkLarge's children list first and then it's added to chunkLarge2's children list.
As you already said, you can use iterator to fix the problem:
Iterator<Node> it = chunkLarge.getChildren().iterator();
while (it.hasNext()) {
// get the next child node
Node node = it.next();
int column = GridPane.getColumnIndex(node);
int row = GridPane.getRowIndex(node);
// remove method is used to safely remove element from the list
it.remove();
// node is already removed from chunkLarge,
// so you can add it to chunkLarge2 without any problems
chunkLarge2.add(node, column, row);
}
Or, without iterator:
// transfer children from chunkLarge to chunkLarge2
chunkLarge2.getChildren().addAll(chunkLarge.getChildren());
// note that you're not iterating over chunkLarge's children list
// (addAll method will make a copy and work with it),
// so it's safe to let the children be automatically deleted
A node instance can only be used as child of a single parent or as root of a single scene. If you add a node as child of a new parent, it's removed from the old parent. This means your loop effectively does the same as the following loop (setting the grid indices is unnecessary, since you want to keep those indices):
for (Node n : chunkLarge.getChildren()) {
chunkLarge.getChildren().remove(n);
chunkLarge2.getChildren().add(n);
}
Since you're modifying the list you iterate through, by means other than the Iterator you get the ConcurrentModificationException.
You need to create new nodes for the second GridPane to display the nodes in both GridPanes.
Related
I am creating a completer myself, using ComboBox and QTreeView (for the proposal list).
MyComboBox::MyComboBox( QWidget *p_parent ) : QComboBox( p_parent )
{
setEditable(true);
m_view = new QTreeView();
m_view->expandAll(); // this command does not work!!!
m_view->setItemDelegate( new CompleterDelegate(m_view));
CompleterSourceModel *m_sourceModel = new CompleterSourceModel(this);
CompleterProxyModel *m_proxyModel = new CompleterProxyModel(this);
m_proxyModel->setSourceModel(m_sourceModel);
setView(m_view);
setModel(m_proxyModel);
connect(this, &QComboBox::currentTextChanged, this, &MyComboBox::showProposalList);
}
The structure of my data for the tree model here is parent-child. With the constructor above, after I put my data into the model, the children are hidden, only the parents can be seen.
In order to see all the items (children) I have to use m_view->expandAll() after I put the data into the model. Is there any way that we can do it in constructor, so each time i put data into the model (whatever my data is), all the items (parents and children) are automatically expanded ?
Your best bet is probably to connect to the QAbstractItemModel::rowsInserted signal to make sure items are expanded on a just-in-time basis. So, immediately after setting the view's model use something like...
connect(m_view->model(), &QAbstractItemModel::rowsInserted,
[this](const QModelIndex &parent, int first, int last)
{
/*
* New rows have been added to parent. Make sure parent
* is fully expanded.
*/
m_view->expandRecursively(parent);
});
Edit: It was noted in the comments (#Patrick Parker) that simply calling m_view->expand(parent) will not work if the row inserted has, itself, one or more levels of descendants. Have changed the code to use m_view->expandRecursively(parent) (as suggested by #m7913d) to take care of that.
Here is a sample code:
public class Example3 {
class Point {
int x, y; // these can be properties if it matters
}
class PointRepresentation {
Point point; // this can be a property if it matters
public PointRepresentation(Point point) {
this.point = point;
}
}
Example3() {
ObservableList<Point> points = FXCollections.observableArrayList();
ObservableList<PointRepresentation> representations = FXCollections.observableArrayList();
points.forEach(point -> representations.add(new PointRepresentation(point)));
}
}
I have a data holder Point and a data representor PointRepresentation. I have a list of points and i would like that for each point in the list there would be an equivalent representation object in the second list. The code I gave works for the initialization but if there is any change later the above will not update.
What I am doing now is using a change listener to synchronize the lists (add and remove elements based on the change object) and it's OK but i am wondering if there's a simpler solution. I was looking for something like a "for each bind" that means: for each element in one list there is one in the other with the specified relation between them [in my case its that constructor]. In pseudocode:
representations.bindForEach(points, point -> new PointRepresentation(point));
Things I looked at: extractors for the list but that sends updates when a property in the objects they hold change and not when the list itself changes. So in my case if x in the point changes i can make an extractor that notifies it. Another thing I looked at is http://docs.oracle.com/javase/8/javafx/api/javafx/beans/binding/ListBinding.html, so maybe a custom binding does it but I don't know if it's simpler.
Also is there a similar solution for arrays instead of lists? i saw the http://docs.oracle.com/javase/8/javafx/api/javafx/collections/ObservableArray.html as a possibility.
The third-party library ReactFX has functionality for this. You can do
ObservableList<Point> points = FXCollections.observableArrayList();
ObservableList<PointRepresentation> representations = LiveList.map(points, PointRepresentation::new);
This will update representations automatically on add/remove etc changes to points.
How to get a child tree node using the name and not the range of the node in the child node list .
i found this method but it uses the position of the element in the children list:
selectedNode.getChildren().get(i).
Many thanks
The collection you receive by calling getChildren() is a standard Java collection IIRC and is not indexed by name. The only ways I can think of realizing this is to create your own Node implementation or to iterate over the collection (which I think is the easiest solution).
public Node getNodeByName(String name)
{
for (Node n : selectedNode.getChildren())
{
if (name.equals(n.getName())
{ return n; }
}
return null;
}
I am new to vaadin and have a databinding problem. I have posted allready in the vaadin forum, but no answer up to now.
if you answer here, I will of course reward it anyway.
https://vaadin.com/forum/-/message_boards/view_message/1057226
thanks in advance.
greets,
Andreas
Additional information: I tried allready to iterate over the items in the container, after pressing a save button. After deleting all original elements in the model collection, and adding copies from the container, the GUI breaks. Some other GUI elements do not respond anymore.
I have personally never used ListSelect, but I found this from the API docs:
This is a simple list select without, for instance, support for new items, lazyloading, and other advanced features.
I'd recommend BeanItemContainer. You can use it like this:
// Create a list of Strings
List<String> strings = new ArrayList<String>();
strings.add("Hello");
// Create a BeanItemContainer and include strings list
final BeanItemContainer<String> container = new BeanItemContainer<String>(strings);
container.addBean("World");
// Create a ListSelect and make BeanItemContainer its data container
ListSelect select = new ListSelect("", container);
// Create a button that adds "!" to the list
Button button = new Button("Add to list", new Button.ClickListener() {
public void buttonClick(ClickEvent event) {
container.addBean("!");
}
}
// Add the components to a layout
myLayout.addComponent(button);
myLayout.addComponent(select);
The downside (or benefit, it depends :) of this is that you can't add duplicate entries to a BeanItemContainer. In the example above the exclamation mark gets only added once.
You can get a Collection of Strings by calling:
Collection<String> strings = container.getItemIds();
If you need to support duplicate entries, take a look at IndexedContainer. With IndexedContainer you can add a String property by calling myIndexedContainer.addContainerProperty("caption", String.class, ""); and give each Item a unique itemId (or let the container generate the id's automatically).
Im not sure I understand your problem but I belive that it might be that you haven't told the controller to repaint. You do this be setting the datasource like this after the save event has occured.
listSelect.setContainerDataSource(listSelect.getContainerDataSource());
I need to add multiple child objects to an existing parent Object. I am instantiating my parent object and sets it Key/Id in my UI processing layer(to which my child objects will be added).
Parent parenttoModify = new Parent();
parenttoModify.Parent_Id = 5; //this comes from some Index of a dropdown or a key column of a grid, i Have put a dummy value here for example
parenttoModify.Children.Add(child);
parenttoModify.Children.Add(child2);
DataAccess.ModifyParent(parenttoModify);
In my data access layer I have a method like :
public static bool ModifyParent(Parent parent)
{
int recordsAffected=0;
using (TestEntities testContext = new TestEntities())
{
testContext.Parents.Attach(parent);
var parentEntry = testContext.ObjectStateManager.GetObjectStateEntry(parent);
parentEntry.ChangeState(System.Data.EntityState.Modified);
recordsAffected = testContext.SaveChanges();
}
return recordsAffected > 0 ? true : false;
}
I get an error when testContext.Parent.Attach(parent) is called. It says:
Object with same key already exist.
I am not sure why is this happening since i am not adding a parent object, I am just attaching it and adding child objects within it.
Any idea where I am going wrong?
Where do you add childs? I guess you are not showing all code. When you call Attach or AddObject EF always attaches or adds all entities from object graph which are not known (tracked) to context at the moment. The exception says that some entity - probably parent - is already tracked by the context. So you have either:
Used shared context (you are creating a new instance in ModifyParent so it should not be a case)
Load parent from the context first in ModifyParent
Called Attach or AddObject on any child before attaching parent.
All these operations lead to the exception you are receiving.