I have a flat data array that comes form a remoteobject, I want to group whatever is to be grouped, but leave single items (the ones with no common data with anything else) alone and without grouping, it's annoying to open each node only to find there's just one item inside, so there was no need to put it inside that group anyway.
Is this something anyone has done? I can't find any reference and I don't know if getting the hierarchicaldata out of the groupingcollection and then iterate thru it would be any good, sounds like a lot of duplicate work.
I ended up doing what shaunhusain said, I created my own copy of groupingcollection and monkeypatched the way it creates the groups, not clean enough for posting or general use yet, but working on it.
can also be accomplished by using a groupitemrenderer and hiding the disclosure icon based
on the number of children.
<mx:AdvancedDataGrid id="adg"
groupItemRenderer="my.namespace.GroupedItemRenderer"
</mx:AdvancedDataGrid>
GroupedItemRenderer is a subclass of AdvancedDataGridGroupItemRenderer
In updateDisplayList :
if (data && data.hasOwnProperty("children")) {
disclosureIcon.visible = (data.children.length > 0);
}
Related
I have a TableView for which I've defined my own itemDelegate. Now, from within this delegate I can access the value for the column using styleData.value, but I'd also need to access the other properties in this same item but I can't find how to.
I need this, because the text styling needs to change depending on some other property of the item model.
Any ideas? thanks!
There is some documentation missing. Within the item delegate you can access the following (taken from the source code of TreeView.qml):
styleData (see documentation)
model (currently not documented)
modelData (currently not documented, not sure about this but I guess it's similar to ListView)
(By the way, what's also missing in the documentation but which is useful is styleData.role. Also, the documentation of the other delegates lacks some available properties too; the best is to peek into the source code of the QML file and have a look for the Loader element which instantiates your delegate. As a plus you learn how that creepy stuff works. ;))
With model and the row/column information you can then navigate to the item data. This code depends on the type of model.
If you're using QML's ListModel, then you can use model.get: model.get(styleData.row)[styleData.role] should then work (untested since I use it rarely, please give feedback).
If you're using a C++ QAbstractItemModel or friends, the best is to add a slot to the model class which takes just the row and role name, since that's the information the TableView works with (nor with role numbers nor with columns...).
However in both cases you shouldn't use the expression in a property binding! The notification system will not work since you don't use the property system for accessing the data. According to your question, I guess you wanted to use it in a expression with binding. I don't know how to properly listen to changes in the model manually.
An alternative approach is to access the other items of the row and provide a property there. Some hints:
From within one item, you can access other items of the same row by walking the object tree up twice (first to the Loader which instantiates your component, then to the actual row) and then down twice (first to the particular child object which is a Loader, then its instantiated item). You need to know the column number you want to access (not the role name), I assume you want to access the first column (index 0):
parent.parent.children[0].item
You can provide the model data using a property in each item. Assuming a simple Text element this might be:
Text {
property variant value: styleData.value // <-- Here you make it available
// your other stuff
}
Putting them together could look like the following. In this example I assume the first row contains an integer, and if it is zero, the second column should be red.
// (within TableView)
itemDelegate: Text {
property variant value: styleData.value
text: styleData.value
color: (styleData.column == 1 && parent.parent.children[0].item.value === 0)
"red" : "black"
}
I think it's pretty easy if you read the source code of TableViewItemDelegateLoader.qml (it is a private code in qtquickcontrol)
To access any role you use use : model[your_role_name] .
For exp: model["comment"]
Faced with same problem today, this is result of my investigations (Qt 5.2.x)
If you have hard limit to TableView, there is only one correct solution - use model.get(styleData.row)["roleForStyling"] as #leemes wrote. But it will very slow if you have big amount of data in model and using, for example, proxy model for sorting/filtering.
Direct solution from #leemes answer is great, but in general case not be working, because in TableView any Item wrapped in Loader and therefore independent from parent and other items:
When some item is created (where you want to change text style)
another element (from which to receive identity) cannot yet be
created
You may not have "parent" on item creation (i.e. binding will
be broken)
In my case, the best solution for deep customise was creation of the simple wrapper for ListView. In this case you have access for complete row data in delegate without the overhead. Highlights for making component ("My own ListView as table"):
Create standalone header (Rectangle or Item) - do not use header form ListView.This make it fixed for any amount of data.
Wrap ListView to ScrollView (if you need scrollbars)
Use Clip: true property in list for make correct
Set style for highlight and set highlightFollowsCurrentItem:true in ListView
As bonus in future this may be used for make "TreeTable" :)
I have some kind of UIComponent grouping which may look something like this with the classes "Group" and "Element".
Groups can have children and children may be elements or groups again, basically similar to a file system or the str+g group function in several graphics programs. The simplest form of a group is a group with only children which are also the most low level groups in the tree.
Edit:
The display hierarchy is already existant, i try to persist it to xml.
Group
- element
- Group
- element
- Group
-element
-element
- element
- element
I want to rebuild this structure in an xml-document for persistence.
I know how to build an xml document in Flex but not how to (recursively) traverse this n-tree correctly.
Update:
For getting only the child nodes one could make use of the following algorithm (pseudo code). But somehow i don't understand how to create the xml from this.
walkTree(group) {
children = node.getChildren
if(children != null) {
for(int i=0; i<children.length; i++) {
if(children[i].isGroup()) {
walkTree(group[i]);
} else {
trace(child);
}
}
}
}
As a starter, I'd suggest this : http://www.sephiroth.it/tutorials/flashPHP/E4X/
So, basically, what you are looking for seems like E4X in Actionscript 3
My suggestion would be to have your structure be data driven from the start, so the XML controls the draw of the screen. Then you would have the XML and wouldn't need to make it.
However, if you really want to do this, you'll need to loop through all the children at each level and add some sort of node that describes the child. However, if you don't have a view that is data driven, I don't see what good this will do you (and if you do, and it's not XML, you're better off writing data export from the data side, not the view side).
i have a Flex tree control and im trying to select a tree node 3 levels down right after the dataProvider is assigned with a collection object like the following.
basically treeItem1, treeItem2, treeItem3 are the nodes in the tree and treeitem3 is a child of treeItem2 which is a child of treeItem1. Assume these treeItem(1,2,3) are referenced correctly from the collection items.
my problem is that if i wait for the whole component to load completely then select the nodes, it open/select/scrolltoIndex correctly. However, if i were to select the node right after the dataProvider is assigned, then it doesn't even open or select (basically the this.treeService.selectedItem is always null).
can anyone point out what i did wrong? is there anything needs to happen after the dataProvider is assigned?
thanks
this.treeService.dataProvider = oPricingHelper.getCurrentPricingSercicesTreeSource();
this.treeService.expandItem(treeItem1, true);
this.treeService.expandItem(treeItem2, true);
this.treeService.selectedItem = treeItem3;
this.treeService.scrollToIndex(this.treeService.selectedIndex);
I have used the updateComplete event to know when a component (such as a DataGroup or List) has completed rendering after performing a simple task (such as updating the dataProvider reference). Of course, you have to be careful and remove listening to updateComplete because it can run a lot, unless you have a need for it to run.
Something like:
//...some function...
this.treeService.addEventListener(FlexEvent.UPDATE_COMPLETE, onTreeUpdateComplete);
this.treeService.dataProvider = oPricingHelper.getCurrentPricingSercicesTreeSource();
//...rest of some function...
private function onTreeUpdateComplete(event:FlexEvent):void {
this.treeService.removeEventListener(FlexEvent.UPDATE_COMPLETE, onTreeUpdateComplete);
this.treeService.expandItem(treeItem1, true);
this.treeService.expandItem(treeItem2, true);
this.treeService.selectedItem = treeItem3;
this.treeService.scrollToIndex(this.treeService.selectedIndex);
}
I'm not positive your experiencing the same issue but I seem to have the same type of problem with using the advanced data grid, it appears in these cases where the dataprovider is acceptable as multiple types, the components do some extra work in the background to wrap things up into something Hierarchical (HierarchicalData or HierarchicalCollectionView) and in doing so the dataprovider setter call is not synchronous (so it will return before actually having assigned the internal property storing the dataprovider). I've used callLater in this case with moderate success, callLater is generally a bad practice but basically adds a function to a list of functions to call once background processing is done, so this is assuming that something in the dataprovider setter called UIComponent.suspendBackgroundProcessing() and that it will subsequently call UIComponent.resumeBackgroundProcessing() and then it will execute the list of functions added by using callLater. Alternatively you could use setTimeout(someFunction,1000).
These are both "hacks" the real solution is to dig into the framework code and see what it's really doing when you tell it to set the dataprovider. Wherever you see that it actually has set the dataprovider you could extend that class and dispatch an event that you could listen for to run the function to do the selections after this point.
If anyone has a better solution please by all means correct me (I would love to have a better answer than this)
I'm having some issues when calling getItemIndex on an ArrayCollection with a filterFunction set.
I do something like myAC.removeItemAt(myAC.getItemIndex(myObject)), which works fine when the filtering hasn't been applied. As soon as filtering is applied, getItemIndex seems to return -1 in every case.
Has anyone come across this before? What the best way to remove an item form a filtered ArrayCollection?
Thanks a lot.
Evan
What exactly is your filter filtering out? If you've filtered out everything, getItemIndex should return -1.
Are you hoping to remove items that are still visible when your filter has been applied? If you still want to remove an item that's filtered out, you could temporarily disable the filter:
var filter:Function = ac.filterFunction;
ac.fiterFunction = null;
ac.refresh();
// remove item
ac.filterFunction = filter;
ac.refresh();
I think you'll find there is a source object within the ArrayCollection. What you are seeing is a view of the underlying data with a sort or filter applied. You really want to delete from the underlying source object.
Any time I've dealt with adding and removing items from ArrayCollections in Flex, I've always kept a copy of the original ArrayCollection. Any adding or removing of items happen to that original copy.
Once the changes have been made to the original, I move those forward to the filtered list.
Remove it from source directly
arrayCollection.source.splice(i, 1)
Yeah, so I did find out that I was changing the property of the object - to one that would have it filtered out - prior to trying to remove it. Of course I would get -1 in that case. My mistake.
Ended up going with your suggestion, Stiggler. Seems to work fine, though it seems like there should be a less hackish way to handle this type of thing. Perhaps a parameter you could pass to removeItemAt that would let you access the unfiltered collection.
Anyway, thanks to both of you for your responses. Much appreciated.
what's the best way to remove a row (QTreeWidgetItem) from a QTreeWidget?
The QTreeWidget content has been set by:
myQTreeWidget->insertTopLevelItems(0, items); // items = QList<QTreeWidgetItem*>
then I remove an item from my QList "items" and I try to clear/reset the QTreeWidget
packList->clear();
packList->insertTopLevelItems(0, items);
but my app crashes here!
Suggestions?
Your problem is that calling packList->clear() deletes the tree widget items contained by the tree. (See the documentation about QTreeWidget::clear(), which includes a note about the items being removed from the tree before deleting.) You'll either need to find a way to remove the items, or not maintain a list of them separately from the tree.
On a slightly-related note, if you are trying to keep track of other data along with the tree, I'd recommend you try to use the models paradigm. In non-trivial cases, it has usually been worth my while to convert to that technique, rather than using the widgets/items.
From what this documentation says, you should be able to do it with:
packList->takeTopLevelItem(index);
Which returns removes and returns the item at the supplied index.