Fast search through QTableWidget rows - c++

I need to search rows through a QTableWidget. Each of the rows in the table contains a field with date and I need to show only rows that are within a specified date interval based on user input. Here is my function:
void nvr::sort()
{
QTableWidget* tabela = this->findChild<QTableWidget*>("NCtable");
QDateEdit* c1 = this->findChild<QDateEdit*>("c1");
QDateEdit* c2 = this->findChild<QDateEdit*>("c2");
// user specified ranges for date
QDate date1 = c1->date();
QDate date2 = c2->date();
//row numbers in table
int rowsNum = tabela->rowCount();
// hide all rows
for(int z = 0; z < rowsNum; z++) {
tabela->hideRow(z);
}
// show only rows that are within range
for(int z = 0; z < rowsNum; z++) {
QDateTime dateTime = QDateTime::fromString(tabela->item(z,2)->text(),"dd.MM.yyyy hh:mm");
QDate date = dateTime.date();
//date compares
if ( (date1.operator <=(date)) && (date2.operator >=(date) ) ) {
tabela->showRow(z);
}
}
}
This works fine if i have 200 rows. But when i have 30 000 rows and i surely will, the gui freezes because i suppose the function executes very slow. Any suggestions for faster execution?

It is hard to reproduce your problem, but here is the approach I would take:
Create a custom class to store the data of one row, let's call it
DataRow.
Store those in a QVector<DataRow>, that you can sort by Date1 for example.
Loop through this QVector<DataRow> and find the elements that correspond to the criteria.
Add those DataRow to a class derived from QAbstractItemModel.
Show this model derived from QAbstractItemModel with a QTableView.
QTableWidget is heavyweight and was not really built for speed. It's really convenient to build something quickly with few elements though.
QTableView is the one you want, with a custom model inherited from QAbstractItemModel.
Then, when the user requests a new input, you could just wipe the model and restart the process. This is not optimal, but the user should not see the difference. Feel free to add more logic here to keep the good elements and only remove the bad ones.
About the GUI freezing, one way to always avoid that is to have the GUI thread separated from other worker threads. The QThread documentation is exhaustive and can help you set up something like this.

Related

How can I get my price vector to = values inside my json array

Hello I am pretty new to json parsing and parsing in general so I am wondering what is the best way I can assaign the correct values for the price of the underlying stock I am looking at. Below is an example of the code I am working with and comments next to them showing kinda what Im confused about
Json::Value chartData = IEX::stocks::chart(symbolSearched);
int n = 390;
QVector<double> time(n), price(n);
//Time and Date Setup
QDateTime start = QDateTime(QDate::currentDate());
QDateTime local = QDateTime::currentDateTime();
QDateTime UTC(local);
start.setTimeSpec(Qt::UTC);
double startTime = start.toTime_t();
double binSize = 3600*24;
time[0] = startTime;
price[0] = //First market price of the stock at market open (930AM)
for(int i = 0; i < n; i++)
{
time[i] = startTime + 3600*i;
price[i] = //Stores prices of specific company stock price all the way until 4:30PM(Market close)
}
the charData is the json output with all the data,
.
I am wondering how I can get the various values inside the json and store them, and also since its intraday data how can I get it where it doesnt store p[i] if there is no data yet due to it being early in the day, and what is the best way to update this every minute so it continously reads in real time data?
Hope I understood correctly (correct me if not) and you just want to save some subset of json data to your QVector. Just iterate through all json elements:
for (int idx = 0; index < chartData.size(); ++idx) {
time[idx] = convert2Timestamp(chartData[idx]["minute"]);
price[idx] = convert2Price(chartData[idx]["high"], chartData[idx]["low"],
chartData[idx]["open"], chartData[idx]["close"], chartData[idx]["average"]);
}
Then you should define what is the logic of convert2Timestamp (how would you like to store the time information) and the logic of convert2Price - how would you like to store the price info, only highest/lowest, only the closing value, maybe all of these numbers grouped together in a structure/class.
Then if you want to execute similar logic every minute to update your locally recorded data, maybe instead of price[idx] = /* something */ you should push additional items that are new to your vector.
If there is a possibility that some of the json keys might not exist, in JsonCPP you could provide a default value e.g. elem.get(KEY, DEFAULT_VAL).

Qt : How to cast QTableRowItem to Object?

I'm new to Qt. i want to cast a row item in the QTableWidget as object.
So far, i've manage to populate the QTableWidget with QList:
header.h
QList<Inventory> inventories;
int row = 0;
int rowCount = ui->tableItems->rowCount();
ui->tableItems->insertRow(rowCount);
foreach(Inventory inventory, this->inventories)
{
QTableWidgetItem *code = new QTableWidgetItem(inventory.getName());
QTableWidgetItem *name = new QTableWidgetItem(inventory.getCode());
QTableWidgetItem *price = new QTableWidgetItem(GlobalFunctions::doubleToMoney(this, inventory.getPrice()));
ui->tableItems->setItem(row,0,code);
ui->tableItems->setItem(row,1,name);
ui->tableItems->setItem(row,2,price);
row++;
}
In my table, i will select the row using this.
void CreateSalesWindow::removeItem()
{
qDebug() << "Remove Item" << ui->tableItems->currentIndex().column();
this->salesdetails.removeAt(ui->tableItems->currentIndex().column() - 1);
this->refreshItemList();
}
I've manage to get the selected row, is there a straightforward way to cast my row back into object. I've come from a C# .Net Background where i could easily cast it back into something like this (just an example). I could not found any good solutions in SO and Documentation.
Inventory selectedInventory = (Inventory) ui->tableItems->selectedItem().getValue();
qDebug() << selectedInventory.getPrice();
// 1699.75
PS. I also want to remove an item from the QList<> from the selected row in the table.
Thanks! I'm new to Qt, i'm open to much better approach in handling this. if something is unclear please raise a comment so i could correct it.
I'm not familiar with the QTableWidget itself, but in general you should use the row method with Qt's model/view classes to access the underlying data row index of the model and then just access your original data from your custom model (depending on the implementation of your model).
In your case something like this:
int rowIndex = ui->tableItems->selectedItems().first().row();
// or this should also work to get the current index directly
int rowIndex = ui->tableItems->currentIndex().row();
Inventory *selectedInventory = ui->tableItems->model()->getInventory(rowIndex);
where the getInventory(int index) method is your custom method to access your object via its index (I guess you have a derived model from QAbstractItemModel or something so save your data and view it in the QTableWidget).
That is at least what I would do, you can read more about general model/view programming with Qt at Introduction to Model/View Programming.

How to make a readable return of data

I read this article http://www.slideshare.net/redigon/refactoring-1658371
on page 53 it states that "You have a method that returns a value but also changes the state of the object. create two methods,one for the query and one for the modification.
But what if on the query I need the values of more than 1 field.
For example:
QSqlQuery query(QSqlDatabase::database("MAIN"));
QString command = "SELECT FIELD1, FIELD2, FIELD3, FIELD4, FIELD5 FROM TABLE";
query.exec( command );
This is the method I know but I really feel that this is not that readable
QString values;
columnDelimiter = "[!##]";
rowDelimiter = "[$%^]";
while( query.next )
{
values += query.value(0).toString() + columnDelimiter;
values += query.value(1).toString() + columnDelimiter;
values += query.value(2).toString() + columnDelimiter;
values += query.value(3).toString() + columnDelimiter;
values += rowDelimiter;
}
And I will retrive it like this.
QStringList rowValues, columnValues;
rowValues = values.split(rowDelimiter);
int rowCtr =0;
while( rowCtr < rowValues.count() )
{
columnValues.clear();
// Here i got the fields I need
columnValues = rowValues.at( rowCtr ).split( columnDelimiter );
// I will put the modification on variables here
rowCtr++;
}
EDIT: Is there a more readable way of doing this?
"Is there a more readable way of doing this?" is a subjective question. I'm not sure whether your question will last long on SO, as SO prefers factual problems and solutions.
What I personally think will make your code more readable, would be:
Use a custom made data structure for your data set. Strings are not the right data structures for tabulated data. Lists of custom made structs are better.
Example:
// data structure for a single row
struct MyRow {
QString a, b, c;
}
...
QList<MyRow> myDataSet;
while( query.next )
{
MyRow currentRow;
// fill with data
currentRow.a = query.value(0).toString();
currentRow.b = query.value(1).toString();
...
myDataSet.append(currentRow);
}
I doubt all your data is text. Some is probably numbers. Never store numbers as strings. That's inefficient.
You first read all data into a data structure, and then read the data structure to process it. Why don't you combine the two? I.e. process while reading the data, in the same while(...)
In your comment, you're confused by the difference between an enum and struct. I suggest, stop doing complex database and QT stuff. Grab a basic C++ book and try to understand C++ first.

Efficient way to make an array of labels

I'm making a board game and I need to display a 15 x 15 array in my gui. I decided to go with individual labels which contains one element of the array. This means I do have quite a lot of labels. I gave each label the name "tile_0_0" with the first 0 being the row and the second 0 being the column. This was quite easy to do in qt.
The problem however is that I ofcourse can't simply use 2 forloops to access each tile since you can't use variables in names. ("tile_i_j" does not exist.) To solve this I decided to make an array which contains each and every label. I however cannot initialise the array efficient because of the earlier mentioned problem.
So the question is: How can I avoid having to write a giant block of code?
A small piece of the current code:
Ui::BoardView *ui = new UI::BoardView; // BoardView is my class
QLabel* m_boardLbArray[8][8];
m_boardLbArray[0][0] = ui->tile_0_0;
m_boardLbArray[0][1] = ui->tile_0_1;
m_boardLbArray[0][2] = ui->tile_0_2;
m_boardLbArray[0][3] = ui->tile_0_3;
m_boardLbArray[0][4] = ui->tile_0_4;
// ...
Note: Sorry that I didn't post a part of code you could simply copy and run, but I do not know how since it's gui related.
It sounds like you are creating your tiles(QLabels) in Qt Designer; A cleaner way to achieve this is to create them programatically. You can do something like add a Grid Layout to your form in Designer in the location you want them, and then do:
QGridLayout *layout = ui->gridlayout;
QLabel* m_boardLbArray[8][8];
for(int row=0; row<8; row++)
for(int col=0; col<8; col++)
{
m_boardLbArray[row][col] = new QLabel(this);
m_boardLbArray[row][col]->setText(tr("This is row %1, col %2")
.arg(row).arg(col));
layout->addWidget(m_boardLbArray[row][col], row, col);
}

How to delete all rows from QTableWidget

I am trying to delete all rows from a QTableWidget . Here is what I tried.
for ( int i = 0; i < mTestTable->rowCount(); ++i )
{
mTestTable->removeRow(i);
}
I had two rows in my table. But this just deleted a single row. A reason could be that I did not create the the table with a fixed table size. The Qt Documentation for rowCount() says,
This property holds the number of rows in the table.
By default, for a table constructed without row and column counts,
this property contains a value of 0.
So if that is the case, what is the best way to remove all rows from table?
Just set the row count to 0 with:
mTestTable->setRowCount(0);
it will delete the QTableWidgetItems automatically, by calling removeRows as you can see in QTableWidget internal model code:
void QTableModel::setRowCount(int rows)
{
int rc = verticalHeaderItems.count();
if (rows < 0 || rc == rows)
return;
if (rc < rows)
insertRows(qMax(rc, 0), rows - rc);
else
removeRows(qMax(rows, 0), rc - rows);
}
I don't know QTableWidget but your code seems to have a logic flaw. You are forgetting that as you go round the loop you are decreasing the value of mTestTable->rowCount(). After you have removed one row, i will be one and mTestTable->rowCount() will also be one, so your loop stops.
I would do it like this
while (mTestTable->rowCount() > 0)
{
mTestTable->removeRow(0);
}
AFAIK setRowCount(0) removes nothing. Objects are still there, but no more visible.
yourtable->model()->removeRows(0, yourtable->rowCount());
QTableWidget test;
test.clear();
test.setRowCount( 0);
The simple way to delete rows is to set the row count to zero. This uses removeRows() internally.
table->setRowCount(0);
You could also clear the content and then remove all rows.
table->clearContents();
table->model()->removeRows(0, table->rowCount());
Both snippets leave the headers untouched!
If you need to get rid of headers, too, you could switch from clearContents() to clear().
In order to prevent an app crash, disconnect all signals from the QTableView.
// Deselects all selected items
ui->tableWidget->clearSelection();
// Disconnect all signals from table widget ! important !
ui->tableWidget->disconnect();
// Remove all items
ui->tableWidget->clearContents();
// Set row count to 0 (remove rows)
ui->tableWidget->setRowCount(0);
Look this post : http://forum.qt.io/topic/1715/qtablewidget-how-to-delete-a-row
QList<QTableWidgetItem*> items = table.findItems(.....);
QMap<int, int> rowsMap;
for(int i = 0; i < items.count(); i++{
rowsMap[items.at(i).row()] = -1; //garbage value
}
QList<int> rowsList = rowsMap.uniqueKeys();
qSort(rowsList);
//Now go through your table and delete rows in descending order as content would shift up and hence cannot do it in ascending order with ease.
for(int i = rowList.count() - 1; i >= 0; i--){
table.removeRow(rowList.at(i));
}
You can just add empty item model (QStandardItemModel) to your QTableView (myTableView):
itemModel = new QStandardItemModel;
ui->myTableView->setModel(itemModel);
In python you can just set the rowCount to zero and that'll work!
tableWidget.setRowCount(0)
This code is tested with PySide6. Hope it will work for PyQt5 and PyQt6 too.
Your code does not delete last row.
Try this one.
int totalRow = mTestTable->rowCount();
for ( int i = 0; i < totalRow ; ++i )
{
mTestTable->removeRow(i);
}
In your code, on the first time, rowCount() have value 2 and value of the i is 0, so its delete 1st row,
But on the second time value of i incremented with 1, but rowCount() return the updated row count which is now 1, so, it does not delete the last row.
Hope now you ll be clear.
Removes all items not in the headers from the view. This will also remove all selections. The table dimensions stay the same.
void QTableWidget::clearContents()
Removes all items in the view. This will also remove all selections and headers.
void QTableWidget::clear()
This works for me:
for i in reversed(range(self.tableWidget.rowCount())):
self.tableWidget.removeRow(i)