NPOI's RemoveRow is very slow - performance

I'm using NPOI to clear data of all rows from a given row index all the way to the last row using the following function:
public void DeleteToLastRow(ISheet sheet, int firstRowIndex)
{
int lastRowIndex = sheet.LastRowNum;
for (int rowIndex = lastRowIndex; rowIndex >= firstRowIndex; rowIndex--)
{
var deletedRow = sheet.GetRow(rowIndex);
// All rows are warrantied to be not null
sheet.RemoveRow(deletedRow);
}
}
However, I have around 100 cells in each row, and around 80 cells out of those 100 have some complex formula. Because of that, it takes me about 8 seconds to delete 200 rows. Is there any way to increase performance in this case?
Most cells reference to 3 or 4 other cells at the start of the same row. They also reference to 1 fixed cell at the start of the sheet that I do not delete.
I'm using .Net 4.6.2, NPOI 2.3.0, my file is xlsx so I'm using XSSF.

Related

How to create a itext table having cells at user specified positions

I have a requirement to add images in an iText PDF table, but the position of cells (consisting of images) will depend on indexes (row and column number) given by the user. This table could also have empty cells in between if no index for any image is given.
How to add a cell in itext pdf at random position?
I looked out for this at various forums, not successful. I would really appriciate the help.
There is no API in iText's Table class to add a cell to an arbitrary position. The reasoning behind such a decision lies in iText's Table architecture: if a table has not been constructed yet (i.e. it's unknown whether there will be some cells with a rowpan and/or a colpan greater than 1), it's not feasible for iText to know whether it could place a cell at some custom position.
However, the good news is that all the layout-related code is triggered only after the table is added to the document. So, one can always do the following:
create a table
fill it with some presaved empty Cell objects
alter some Cell object as requested
add the table to the document
In the snippet above I will show, hot to add some diagonal content to the table after the cells have been added to it. The same approach could be followed for images.
int numberOfRows = 3;
int numberOfColumns = 3;
Table table = new Table(numberOfColumns);
List<List<Cell>> cells = new ArrayList<>();
for (int i = 0; i < numberOfRows; i++) {
List<Cell> row = new ArrayList<>();
for (int j = 0; j < numberOfColumns; j++) {
Cell cell = new Cell();
row.add(cell);
table.addCell(cell);
}
cells.add(row);
}
// Add some text diagonally
cells.get(0).get(0).add(new Paragraph("Hello"));
cells.get(1).get(1).add(new Paragraph("diagonal"));
cells.get(2).get(2).add(new Paragraph("world!"));
The resultant PDF looks as follows:

QTableWidget resizeRowsToContents very SLOW

I've a QTableWidget that contains over 200.000 rows and 8 columns.
The columns has a fixed size.
The rows has a variable size.
Adding the items to the table is fast (few seconds).
Then the call of resizeRowsToContents() takes almost 60/120 seconds! I see that this call is single thread! (Only one core works).
Can I force MultiThreading ?
How Can I speedup the row resizing ?
Thank you,
Salvo
One thing you might try is to update the QTableWidget row-by-row using QTableWidget::resizeRowToContents(Note: Row rather than Rows) with the updates being interleaved with other events in the queue. Overall the process would still take the same amount of time but your GUI would remain responsive during the process.
Firstly, take advantage of the fact that a QTimer with a zero timeout will emit its timeout signal whenever the event queue becomes empty. So code such as...
QTimer::singleShot(0, &my_callback);
will effectively wait until the event queue is empty and then invoke my_callback. If my_callback calls the same line of code then you have a function that automatically invokes itself during the next idle period.
Now write the function...
void resize_row (QTableWidget *view, int row, int count = 1)
{
/*
* Resize rows `row' -> `row + count - 1'
*/
for (int todo = count; row < view->rowCount() && todo--; ++row) {
view->resizeRowToContents(row);
}
/*
* If there are still rows remaining then reschedule.
*/
if (row < view->rowCount()) {
QTimer::singleShot(0, [=](){ resize_row(view, row, count); });
}
}
This will invoke view->resizeRowToContents(...) on all rows in the range [row, row + count). If the row index is still less that the row count it will schedule itself for the next idle period with updated parameters.
Now, replace...
table_widget->resizeRowsToContents();
with...
QTimer::singleShot(0, [table_widget, row = 0](){ resize_row(table_widget, row, 10); });
Seems to work fine in the basic tests I've done.
Resize has to re-calculate, each row hight and adjust. I would not recommend having this many rows in a single QTableWidget. I would go for a pagination technic or some dynamic loading functionality for this long list.
If you want more speed use QAbstractTableModel instead of QTableWidget and override the required functions. This is a simple and good example:
https://doc.qt.io/qt-5/modelview.html

Iterate through jqGrid update every row for a column

I wish to iterate through jqGrid, and for a given column (ie: the second) I wish to insert a value. How do you find the first data row? The documentation warns to not use getRowData if updating row or cell data.
This is what I'm using, but it seems clumsy:
function loadCompleted() {
var $grid = jQuery("#jqGrid"), rows = $grid[0].rows;
for (var i = 1; i < rows.length; i++) {
var row = rows[i];
var id = row.cells[0].innerHTML;
$(row.cells[1]).html("<a href='#' onclick='deleteApp(" + id + "); return false;'>Delete</a>");
}
}
... this works, but it makes the assumption that the first data row is the second row in table #jqGrid. It also relies on index values for columns 1 and 2.
Is there any way to use setRowData when the documentation warns to not use getRowData when editing the row or cells?
The first row in grid is hidden and is used in the jqGrid for internal purposes.
I think that using a custom formatter will do the job.
Example of custom formatter can be found here. If you use Guriddo jqGid you may look into the docs for the parameters passed.

GetColumn Information and Width on Resize - Slick Grid

Am working on slick grid where am trying to get the column information like id,name and the new width of column after resize.
I wrote an event which will be triggered when user resizes the column.
grid.onColumnsResized.subscribe(function (e, args) {
//To Do
});
grid.getColumns() will help but how do i identify which column user has resized. is there a way I can get the column index of the resized column?
some start up code from here will save lot of my time
Thanks
The onColumnsResized event triggered by SlickGrid doesn't include any references to the columns which changed.
It's important to note that the width of multiple columns may have changed when this event triggers. Examples of this are:
Using the grid option forceFitColumns: true to force the columns to fit in the width of the grid
Resizing a column so small it affects the columns to its left
Two possible options for implementing this are:
Check columns after change
SlickGrid stores the previous column widths in the property named previousWidth on each column. You can compare the prevoiusWidth and width values to determine which columns changed.
grid.onColumnsResized.subscribe(function (e, args) {
//Loop through columns
for(var i = 0, totI = grid.getColumns().length; i < totI; i++){
var column = grid.getColumns()[i];
//Check if column width has changed
if (column.width != column.previousWidth){
//Found a changed column - there may be multiple so all columns must be checked
console.log('Changed column index : ' + i);
console.log(column);
}
}
});
SlickGrid resets the previousWidth values for all columns whenever a column starts being resized.
You can view an example of this approach at http://plnkr.co/edit/W42pBa2ktWKGtqNtQzii?p=preview.
Modifying SlickGrid
If you are hosting SlickGrid and are comfortable maintaining your own version then you could modify it to include column information in the args of the onColumnsResized event.
In slick.grid.js at line 860 amend the code where the event is triggered to include an array containing the indexes of changed columns. You can also include the index of the column which the user resized if this is useful. The below adds properties named changedColumnIndexes and triggeredByColumnIndex which are passed in the args of the triggered event. I've wrapped the changes for this in comments prefixed //MODIFICATION.
.bind("dragend", function (e, dd) {
var newWidth;
//MODIFICATION - Add array to capture changed column indexes and variable to capture
// the index of the column which triggered the change
var changedColumnIndexes = [];
var triggeredByColumnIndex = getColumnIndex($(this).parent()[0].id.replace(uid, ""));
//MODIFICATION END
$(this).parent().removeClass("slick-header-column-active");
for (j = 0; j < columnElements.length; j++) {
c = columns[j];
newWidth = $(columnElements[j]).outerWidth();
//MODIFICATION - Add column index to array if changed
if (c.previousWidth !== newWidth) {
changedColumnIndexes.push(j);
}
//MODIFICATION END
if (c.previousWidth !== newWidth && c.rerenderOnResize) {
invalidateAllRows();
}
}
updateCanvasWidth(true);
render();
//MODIFICATION - Amend trigger for event to include array and triggeredBy column
trigger(self.onColumnsResized, {changedColumnIndexes: changedColumnIndexes, triggeredByColumnIndex: triggeredByColumnIndex});
//MODIFICATION END
});
In your own code subscribe to the onColumnsResized event and pickup the changed column index from the args of the event.
grid.onColumnsResized.subscribe(function(e, args) {
//Triggered by column is passed in args.triggeredByColumnIndex
console.log('Change triggered by column in index : ' + args.triggeredByColumnIndex);
console.log(grid.getColumns()[args.triggeredByColumnIndex]);
//Column array is passed in args.changedColumnIndexes
console.log('Changed columns are...');
console.log(args.changedColumnIndexes);
//Loop through changed columns if necessary
for (var i = 0, totI = args.changedColumnIndexes.length; i < totI; i++){
console.log(grid.getColumns()[args.changedColumnIndexes[i]]);
}
});
You can view an example of this approach at http://plnkr.co/edit/4K6wRtTqSo12SE6WdKFk?p=preview.
Determining column changes by column.width != column.previousWidth wasn't working for me because sometimes the original and new width were different by an insignificant size (e.g. 125.001 and 125).
I used Chris C's logic and made a PR on the 6pac/SlickGrid project. Here's the commit:
https://github.com/6pac/SlickGrid/commit/ba525c8c50baf18d90c7db9eaa3f972b040e0a6e

Google charts column filtering

I am trying to filter a Google line chart columns and using the code shared here in Google Charts-Code for Category Filter
It all works well however I have a number of columns and would like to have the chart start with just one column displayed and allow the user to add in any of the additional columns as needed.
What I've found is that if I play with the initState variable to set it to the one column I want to display initially, it will have that column shown in the selector section but still displays all the columns initially until I select an additional column when it hides the rest and just displays the two I have selected.
So then I tried turning off this part of the code:
// put the columns into this data table (skip column 0)<br>
for (var i = 1; i < data.getNumberOfColumns(); i++) {
columnsTable.addRow([i, data.getColumnLabel(i)]);
initState.selectedValues.push(data.getColumnLabel(i));
}
and replacing it with
columnsTable.addRow([1, data.getColumnLabel(16)]);
initState.selectedValues.push(data.getColumnLabel(16));
which sets the column i'm after (column 16) as the selected column in the selection list but removes the other columns from the list of available columns and still displays all 16 columns.
How can I set this up so it displays the single selected column's data initially yet still gives the ability to pick other columns from the selector?
You want to keep the columnstable.addRow call inside the for loop, as it populates the DataTable used to provide the list of columns. You can set the selectedValue variable as you have it:
// put the columns into this data table (skip column 0)<br>
for (var i = 1; i < data.getNumberOfColumns(); i++) {
columnsTable.addRow([i, data.getColumnLabel(i)]);
}
initState.selectedValues.push(data.getColumnLabel(16));
In order to make the chart draw properly with the initial selection, we need to make a small adjustment to the structure, putting all of the updating code into its own function, and then calling that as necessary:
function setChartView () {
var state = columnFilter.getState();
var row;
var view = {
columns: [0]
};
for (var i = 0; i < state.selectedValues.length; i++) {
row = columnsTable.getFilteredRows([{column: 1, value: state.selectedValues[i]}])[0];
view.columns.push(columnsTable.getValue(row, 0));
}
// sort the indices into their original order
view.columns.sort(function (a, b) {
return (a - b);
});
chart.setView(view);
chart.draw();
}
google.visualization.events.addListener(columnFilter, 'statechange', setChartView);
setChartView();
Here's a working example: http://jsfiddle.net/asgallant/WaUu2/157/.

Resources