Qt Sort using lessThan is extremely slow - qt

I have an QTableView with QSortFilterProxyModel (on QFileSystemModel). I sort it using lessThen comparator like this:
bool SortProxy::lessThan ( const QModelIndex & left, const QModelIndex & right ) const
{
...
return (collator.compare(left.data().toString(),right.data().toString())) > 0;
...
}
And it works fine for sorting files by name, or filetype. But it works extremely slow on sorting files by name. What to do?

Related

QTableView scrolling stopped after dynamic resizing

I have a QTableView with a custom QSortFilterProxyModel for searching and sorting and a QSqlQueryModel for populating the table.
void ProxyModel::searchTable(QString name, QString type, QString date, QString time ){
if(name_ != name)
name_ = name;
if(type_ != type)
type_ = type;
if(date_ != date)
date_ = date;
if(time_ != time)
time_ = time;
invalidateFilter();
}
bool ProxyModel::filterAcceptsRow(int source_row,
const QModelIndex &source_parent) const{
QModelIndex indName = sourceModel()->index(source_row,
0, source_parent);
QModelIndex indType= sourceModel()->index(source_row,
4, source_parent);
QModelIndex indDate = sourceModel()->index(source_row,
2, source_parent);
QModelIndex indTime = sourceModel()->index(source_row,
3, source_parent);
if(
sourceModel()->data(indName).toString().toLower().contains(name_.toLower())
&&sourceModel()->data(indType).toString().toLower().contains(type_.toLower())
&&sourceModel()->data(indDate).toString().toLower().contains(date_.toLower())
&&sourceModel()->data(indTime).toString().toLower().contains(time_.toLower())
)
{
emit adjust();
return true;
}
return false;
}
After a successful search, I emit a signal from my proxy model to a slot where it adjusts the table height to fit the size of the rows.
connect(proxyModel, SIGNAL(adjust()), this, SLOT(dataChanged()));
And when the search button is clicked
connect(ui->searchBtn, &QToolButton::clicked, this, &AllVisitedPlaces::getSearchOptions);
I call the proxy model searchTable method with search parameters
void AllVisitedPlaces::getSearchOptions()
{
proxyModel->searchTable(ui->nameLineEdit->text(),
ui->typeLineEdit->text(),
ui->dateLineEdit->text(),
ui->timeLineEdit->text());
adjustTableSize();
}
void AllVisitedPlaces::dataChanged()
{
adjustTableSize();
this->verticalHeader->setSectionResizeMode(QHeaderView::ResizeToContents);
}
void AllVisitedPlaces::adjustTableSize()
{
QRect rect = ui->table->geometry();
int height = 0;
for (int i =0; i < proxyModel->rowCount() ; i++)
height+= ui->table->rowHeight(i);
rect.setHeight(18 + ui->table->horizontalHeader()->height() + height);
ui->table->setGeometry(rect);
verticalHeader->setSectionResizeMode(QHeaderView::Stretch);
}
The problem is, when the table re-sizes , I lose scrolling.
How can I fix that ?
Before re-sizing :
After re-sizing :
Why would you expect scrolling when you've resized the table to fit its contents?
The likely problem is that your Ui is broken in other ways and the table is obscured: it has been resized, but you can't see that because whatever widget the table view sits in doesn't manage the table widget properly. But we can't tell for sure because you didn't minimize your code to provide a complete compileable example of what you're doing. We're talking about <100 lines of code in all - surely it wouldn't be a big deal to just paste such a main.cpp in your question. See e.g. example 1 or example 2.
This is a bad design anyway since you're presuming that the table will fit on screen. Yet it won't: once the resizing works, you'll end up with a vertically huge window that cannot be used as some of its corners will extend past the screen, with no way to reach them to see the contents or to resize the window.
Finally, once you properly use layouts in your Ui design, the setGeometry() call on any widget below top-level is a no-op: it's the layout that controls the child widget geometry. The solution then is not to set the widget's geometry, but to set its minimum size instead.
You're facing an XY Problem: you're dead set on a solution, without telling us what it is that you're trying to achieve and making sure first that what you're after makes sense (as in: that it will actually lead to a usable Ui!).

QTableView: how to edit non-editable cells in the program?

How should this be done by using the model->setData() method call?
I have derived a class called "MyStandardItemModel" from QStandardItemModel. I have made my third and fourth columns non-editable by overriding the protected virtual flags method. This is how it goes:
#define TX_PACKET_COLUMN (4u)
#define RX_PACKET_COLUMN (5u)
Qt::ItemFlags MyStandardItemModel::flags(const QModelIndex& index) const
{
if (index.column() == TX_PACKET_COLUMN || index.column() == RX_PACKET_COLUMN)
{
return (QStandardItemModel::flags(index) & ~Qt::ItemIsEditable);
}
else
{
return QStandardItemModel::flags(index);
}
}
...
//Set model
ui->testCaseTableView->setModel(model);
Having this done, I am not able to edit the cells in the third and fourth column.
Now, I want that when I double click on these cells, a pop-up dialog comes up. I will modify some data in the editable field of that dialog, and then copy it back to the non editable cells inside the code.
I tried to just write a doubleclick() handler for the QTreeView and just copy some data to the cells just to see if it is possible to copy data to the non-editable cells.
This operation is failing, and the data is not written into the non-editable cells.
Here you can find the double click handler:
void MainWindow::on_testCaseTableView_doubleClicked(const QModelIndex &index)
{
QVariant variant;
variant.toString() = "AA";
if((index.column() == TX_PACKET_COLUMN)||(index.column() == RX_PACKET_COLUMN))
{
model->setData(index, variant); // set new value
}
}
The setData(..) operation is clearing the already written data in the cells, but string "AA" is not getting written. Please suggest how to copy some data to non-editable cells inside the code.
QVariant set is empty. Nothing needs to be wrong in your model. Error is on this line:
variant.toString() = "AA";
change to:
QVariant variant("AA"); // just for testing anyway
As I indicated in my comment, you have to fix this first issue:
instead of:
QVariant variant;
variant.toString() = "AA";
you should write
QVariant variant = QLatin1String("AA");
In general, you would look into the setData(...) implementation for such cases whether you emit the data changed signal properly and so forth, but here you are entering a prior issue which can lead to problems, so let us fix that.
Note, you should use QLatin1String to avoid the unnecessary explicit conversion from raw char* to QString. This is a good practice in general, and this is available with Qt 4 as well as Qt 5.
Although, you could also use the QStringLiteral macro for creating a QString very efficiently with template magic out of your raw literal, but that requires C++11.

Qt. Redefine QString::compare in heavy applications

My task is custom sorting items in QStandardItemModel.
By default for a sequance of rows
text1
text11
text12
text100
text110
text120
text1110
function QStandardItemModel::sort() sorting it as
text1
text100
text11
text110
text1110
text12
text120
I want that would be
text1
text11
text12
text100
text110
text120
text1110
For this purpose I overload function int QString::compare(const QString &s) const in the separeted compare.cpp file.
int QString::compare(const QString &s) const
{
QString strL = "";
strL.append(this);
QString strR = "";
strR.append(s);
QStringList list = strL.split(QRegExp("\\D+"),QString::SkipEmptyParts);
foreach (QString num, list) {
strL.replace(num, QString("%1").arg(num,10,'0'));
}
list = strR.split(QRegExp("\\D+"),QString::SkipEmptyParts);
foreach (QString num, list) {
strR.replace(num, QString("%1").arg(num,10,'0'));
}
return strL.localeAwareCompare(strR);
}
that using in operator
virtual bool operator< ( const QStandardItem & other ) const.
Such function as compare can be redefined in the separate file and it is simple to add it in *.pro and easy application will find its realization. But with more difficult applications such way it is impossible. Please tell me Why?
Example : code
Rather than using a QStandardItemModel directly, you want to wrap it in a QSortFilterProxyModel. This class was designed for exactly the situation you describe--when you want to implement custom sorting or filtering behavior. Just implement the QSortFilterProxyModel::lessThan method to reflect the desired behavior.
QStandardItemModel has a virtual function sort(int column, Qt::SortOrder order = Qt::AscendingOrder). I think it will be easier to subclass QStandardItemModel and reimplement sortfunction.
I did!
When i said "heavy application", i means application, that contains and binds many other plugins and libs. And for this purpose what I create the new lib with name QStringCompare, that contains one file compare.cpp with my new definiotoin of compare:
#include <QStringList>
#include <QRegExp>
int Q_DECL_EXPORT QString::compare(const QString &s) const
{
QString strL = "";
strL.append(this);
QString strR = "";
strR.append(s);
QStringList list = strL.split(QRegExp("\\D+"),QString::SkipEmptyParts);
foreach (QString num, list) {
strL.replace(num, QString("%1").arg(num,10,'0'));
}
list = strR.split(QRegExp("\\D+"),QString::SkipEmptyParts);
foreach (QString num, list) {
strR.replace(num, QString("%1").arg(num,10,'0'));
}
return strL.localeAwareCompare(strR);
}
and it links QStringCompare.lib to *.pro of Main apllication of my project. Generally it's not necessary to declare it in *.h files. All other plug-ins inherit this redefinition. The experiments showed that it is necessary to links to main application.
As that so.
There can be I am mistaken in reasonings, but it's working on linux and windows.
This is source of QStringCompare.libs. You can try.

QFileSystemModel sorting DirsFirst

How do you do to sort a QFileSystemModel with QDir::DirsFirst like in QDirModel?
The QFileSystemModel does not have a setSorting method.
Maybe somebody will need this. I have implemented directories first sorting using QSortFilterProxyModel for QFileSystemModel as Kuba Ober mention in comment.
Might be not perfect yet, but still right direction.
bool MySortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
// If sorting by file names column
if (sortColumn() == 0) {
QFileSystemModel *fsm = qobject_cast<QFileSystemModel*>(sourceModel());
bool asc = sortOrder() == Qt::AscendingOrder ? true : false;
QFileInfo leftFileInfo = fsm->fileInfo(left);
QFileInfo rightFileInfo = fsm->fileInfo(right);
// If DotAndDot move in the beginning
if (sourceModel()->data(left).toString() == "..")
return asc;
if (sourceModel()->data(right).toString() == "..")
return !asc;
// Move dirs upper
if (!leftFileInfo.isDir() && rightFileInfo.isDir()) {
return !asc;
}
if (leftFileInfo.isDir() && !rightFileInfo.isDir()) {
return asc;
}
}
return QSortFilterProxyModel::lessThan(left, right);
}
As far as I can tell, you can't (in Qt4).
The default sort order (by the "name" column), or sorting by size behaves like QDir::DirsFirst (or DirsLast if in reverse order for ), but sorting by time or type doesn't treat directories differently from ordinary files.
The QFileSystemModel doesn't expose an API for changing the sort order, and I don't see any opportunity for influencing it in the QFileSystemModel code.
(I don't see anything in the current Qt5 docs to indicate that this has changed, but those aren't final and I haven't looked very closely.)

sort row by column , when getting text always wrong

I'm trying to get text and data from column in index number 0 from row that is selected
but I never get the right data I'm using simple model view TreeView with QSortFilterProxyModel proxy to sort the columns and QStandardItemModel as the model
This is the slot function that is triggered on each doubleClicked
connect(ui.treeView_mainwindow, SIGNAL(doubleClicked( const QModelIndex &)), this,SLOT(tree_itemClicked( const QModelIndex &)));
....
...
void MainWindowContainer::tree_itemClicked(const QModelIndex & index)
{
int iSelectedRow = index.row();
QString groupID;
QString groupName;
groupID = m_model->item(iSelectedRow,0)->data(Qt::UserRole).toString();
groupName = m_model->item(iSelectedRow,0)->text();
}
UPDATE:
Well, I found the answer but I have another question, the answer is :
QString groupID = index.model()->index(index.row(), 0, index.parent()).data(Qt::UserRole).toString();
QString groupName = index.model()->index(index.row(), 0, index.parent()).data(Qt::DataRole).toString();
}
My other question is how do I set data to column in index ( for example: 3 ) in the selected row?
The problem here most likely is that index.row() points to the row in the proxy model after sorting. This is most likely not the same row in your unsorted source model.
Try the following instead:
groupID = m_proxy_model->index(iSelectedRow,0).data(Qt::UserRole).toString();
Have you tried using
QStandardItem * QStandardItemModel::itemFromIndex ( const QModelIndex & index ) const;
Perhaps your rows are not set properly.
If this does not help, you should give an example tree, indicate what you click, what you expect, what you get.
QModelIndex modelIndex = m_proxy_model->index(iSelectedRow,0);
m_proxy_model->data (modelIndex ,Qt::UserRole).toString();

Resources