performance of loops iterating over selectedItems() - qt

I had an issue with setting items as selected... so I took a peek at the QGraphicsScene::selectedItems() function.
Ever since, I became really nervous about using it in loops.
In a construct like this:
foreach(QGraphicsItem* selectedItem, selectedItems())
{
// perform action with selectedItem
}
Will every iteration recalculate the selectedItems() function ?
(I think so, because the above becomes unstable if inside the loop I change selection)
I imagine this would have a big impact on my code speed...
So I am starting to change me code everywhere, to:
QList<QGraphicsItem*> sel = selectedItems();
foreach(QGraphicsItem* selectedItem, sel)
{
// perform action with selectedItem
}
Am I correct in assuming this will speed up the program ?
(or will it make it slower because of copying, while the replacement in loop test would not change, if I am wrong and the selectedItems() doesn't really get through all its code ?)
I wonder what other functions should be avoided... like perhaps sceneRect() or boundingRect() for items inheriting from QGraphicsItem.... Is it right to copy those to a QRectF if used more than once in the same function ?

foreach(QGraphicsItem* selectedItem, selectedItems())
{
// perform action with selectedItem
}
In this case, you're iterating over the returned selected items and as you've discovered, if the selection is changed, this can cause problems.
Is this better?
QList<QGraphicsItem*> sel = selectedItems();
foreach(QGraphicsItem* selectedItem, sel)
{
// perform action with selectedItem
}
The simple answer is yes.
will it make it slower because of copying
Not necessarily. If the container is unchanged, it won't make a difference.
However, Qt implements implicit sharing, which ensures that a container, such as QList will share the data in this case and only make a copy (COW - copy on write), when one of the copies changes.
I wouldn't worry too much over sceneRect and boundingRect, as they just return simple data types such as QRectF, rather than a container of items, so implicit sharing is not required here. Only in extreme circumstances would caching such returned values make a noticeable difference.

Related

optimization: vector.erase() of pointer

I have a question about deleting a dynamic vector of pointers and optimization.
Here is my code. It checks wether an element has to be set to nullptr and then it delete all those elements.
for (auto* el : elements)
{
if (el != 0)
// do something
else
el = nullptr;
}
elements.erase(std::remove(elements.begin(), elements.end(), nullptr), elements.end());
Is the complexity of this operation onerous for the machine ?
And if it is, then is there a better way of doing it and it is worth it ? Because, here, the preservation of the index order is not important for me.
Thank you !
Is the complexity of this operation onerous for the machine ?
It is a bit costly, but not much more than the previous operation. Indeed, remove will typically check the value of each item, and if an item needs to be removed, the algorithm shifts the item on the right to put it on the current analysed item. erase is often relatively cheap since it just resizes the vector to skip the remaining garbage at the end (generally without any copy or reallocation) and call the destructor of the discarded items (costly only if there is a lot of them and the destructor is non-trivial). This operation can be as costly as the previous one.
And if it is, then is there a better way of doing it and it is worth it ? Because, here, the preservation of the index order is not important for me.
Yes, this is possible: you can just iterate over the array with a classical loop and swap the current item with the one of the end to discard it. You need to maintain a end iterator moving from the end to the beginning. The loop stops when the end iterator is reached. Note that the swapped items coming from the end should be checked by your predicate too.
Alternatively, you could just use std::partition at this algorithm does a quite similar job and is simpler: it puts the items validating a given condition to the left part and put the other on the right part. You can then just resize the array to remove the unwanted right part.
std::partition should is bit less efficient than the other swap-based approach only if there is a lot of item to remove since it has to maintain the consistency of both sides.
Here is an (untested) example with std::partition:
auto discardedBegin = partition(elements.begin(), elements.end(), doSomething);
elements.erase(discardedBegin, elements.end());

QTableView not updating properly

I am creating a small program that takes user input into a model and then shows that input in several views that take it through filters.
When the user clicks the button that accepts the input, the program updates the amount of cells in the views and then resizes those cells as necessary so that they fit neatly in their area.
My problem is that the cell resizing doesn't seem to work for one of the views for some reason (I tried looking for differences but couldn't find a reason for what I'm experiencing).
I'm calling the cell resizing function in two places:
dataChanged slot.
resizeEvent slot.
If the cell resize function gets called twice inside dataChanged, then the view does update, however this involves some calculations and ui access and obviously not supposed to happen.
If I resize my window then the cells are resized properly.
I suspect that I'm always one update behind - that the view doesn't paint until the new update starts getting calculated and then that new update is on hold until the next calculation (since resize happens a lot of times in succession it might just act the same as the button but is harder/impossible to notice).
I have some dirty workarounds:
As I mentioned, if I call my cell resize function again, the view updates properly.
If I remove the second "if" in this next piece of code then everything works.
I thought I'd save my computer some work by only processing when the entire input had been received. My thinking was that, although dataChanged is emitted for every single item I'm inserting, I only really need to update once it is all in:
void MainWindow::on_dataChanged()
{
static int left_to_insert = -1;
if ( 0 > left_to_insert )
{
left_to_insert = m_model.rowCount() - 1;
}
if ( 0 == left_to_insert )
{
...
m_matrix_proxy.resize_to_fit();
adjust_matrix_cells_sizes();
}
--left_to_insert
}
Is it bad to only process the last signal? Why?
I tried calling update() and/or repaint() on both the matrix and the main window.
I tried calling both of these on the viewport of the QTableView and tried calling them in succession from the matrix itself to the highest parent that didn't make my program crash. (ui->matrix->parentWidget()->parentWidget()...)
I tried qApp->processEvents().
I even resorted to emitting a resizeEvent, but this is overkill IMO as it makes some calculations be performed again.
Just in case it is somehow relevant: The data appears correctly. The only thing that's wrong is that the cells don't resize.
You need to emit layoutChanged signal from your model. But be care with large amounts of items, because handling of this signal may take a lot of time.
Similar questions: one, two
This logic in only code sample you have given is wrong. And this static keyword makes it even worse.
Actual answer:
There is ready solution delivered by Qt! See documentation of QHeaderView::ResizeMode and QHeaderView::setSectionResizeMode
Old answer:
IMO this should look like this:
void MainWindow::MainWindow()
…
{
…
mNeetToResizeCells = false;
connect(this, &MainWindow::NeedUpdateCellsSizes,
this, &MainWindow::ResizeTableCells,
Qt::QueuedConnection); // this is imporatant
}
void MainWindow::on_dataChanged()
{
if (!mNeetToResizeCells) {
mNeetToResizeCells = true;
emit NeedUpdateCellsSizes();
}
}
void MainWindow::ResizeTableCells()
{
mNeetToResizeCells = false;
// update cells sizes here
ui->tableView->resizeColumnsToContents();
ui->tableView->resizeRowsToContents();
}
This way all data updates performed in one iteration of event loop will cause only one invocation of MainWindow::ResizeTableCells in some future iteration of event loop.

Flex Mobile validateNow() not working

I have a long and busy for loop which is supposed to addElement on the stage iteratively. Since it takes several seconds to execute the whole loop (i=1:N), i just want to refresh the stage at each loop so that the output is displayed before the loop reaches its final point (N). each iteration should add a displayable element before the next iteration begins.
For this i wrote the following
for(var i:int = 0; i < 280; i++){
addElement(...);
validateNow();
}
but it is not working like i want. anyone having solution please?
You need to divide up this lengthy work so that it can occur over multiple frames. Flash/Flex do not update the screen when your code is executing. Have a look at the elastic race track for Flash to help understand why. The Flex component life cycle adds another layer on top of that as well. By the way, calling validateNow() can be computationally expensive, possibly making your loop take longer :)
There are a number of ways to break up the work in the loop.
Here's a simple example using callLater():
private function startWorking():void
{
// create a queue of work (whatever you are iterating over)
// in your loop you're counting to 280, you could use a
// simple counter variable here instead
var queue:Array = [ a, b, c];
callLater(doWork, [ queue ] );
}
private function doWork(workQueue:Array):void
{
var workItem:Object = workQueue.shift();
// do expensive work to create the element to add to screen
addElement(...);
if (workQueue.length > 0)
callLater(doWork, workQueue);
}
You may want to process more than 1 item at a time. You could also do the same thing using the ENTER_FRAME event instead of callLater() (in essence, this is what callLater() is doing).
References:
validateClient() docs, calling validateNow() results in a call to this expensive method

objective-c if-else statement

I have both a UITextLabel and a UITextView occupying the same real estate, point-for-point in my view. I have an NSMutableArray allocated to populate the UITextLabel based on what the user taps in a table. I would also like to program the app to use the UITextView if and only if the user taps on one specific row. I think this can be accomplished with an if-else (if) statement, but I'm not sure. If that will work, I don't know what to put in the if part. So far, I have this:
if (qux == ??) {
fooTextView.text = textString;
} else {
fooTextLabel.text = textString;
}
I don't know what to set the if statement to. I tried to set it to
if (qux == the really long code containing the single array statement
I want displayed as a UITextView)
but that returned an error. Does anyone have a suggestion on how to get Xcode to recognize my single NSMutableArray table cell with the UITextView and not (also) with the UILabel? As of right now, the app builds but displays the text both in the UITextView, so it scrolls and does exactly what I want but also displays in the UILabel and is static and visible "underneath" the scrolling UITextView.
Also, I'm using an NSMutable Array with initWithObjectAndKeys. In this special case that is the only one of its kind in my UITableView, I use a special key that is only used by this particular part of the array. Is it possible to set it up so it's something regarding:
if (qux == *NSMutableArray with the special key*) {
fooTextView.text = textString;
} else {
fooTextLabel.text = textString;
}
And should I be using an if-else if statement or just an if-else statement?
Use the UITableView Delegate method didSelectRowAtIndexPath: you can get you row index from the NSIndexPath (indexPath.row). The you can use an if-then or a switch to determine which of your controls gets the data. Make sure you set your UITableView's delegate property to your file owner so the UITableView will call back to you controller.
HTH

Flex: select tree node right after the dataProvider is been assigned / updated / replace

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)

Resources