Custom Sorting in QTableWidget - qt

I have a QTableWidget and i am using its default sorting capability through header columns but one of my column in QTableWidget is integer type and through QTableWidget default sorting it is being sorted like a string.So there is any means by which i can use my own sorting functions for QTableWidget?

You can try to subclass the QTableWidgetItem and reimplement operator<() of it. Than in your QTableWidget use this custom items instead of default QTableWidgetItems. Something like this:
class Item: public QTableWidgetItem
{
public:
[..]
bool operator< (const QTableWidgetItem &other) const
{
// TODO: To be safe, check weather conversion to int is possible.
return (this->text().toInt() < other.text().toInt());
}
[..]
};
And in your table widget:
[..]
QTableWidgetItem *newItem = new Item("1");
tableWidget->setItem(row, column, newItem);
[..]

I am not sure, but I don't think there is an easy way to change the sorting behaviour of a QTableWidget.
QTableWidget is just a convenience class for QTableView, which uses a default model. No guarantee, but what would try to do:
QTableWidget inheris the model() method from QTableView. With it you should be able to get the widget's model:
QAbstractItemModel *model = yourTableWidget->model();
This was the easy part. You now need a custom QSortFilterProxyModel, where you can override the virtual bool lessThan(const QModelIndex & left, const QModelIndex & right) const method.
And finally:
YourCustomFilterProxyModel *proxyModel = new YourCustomFilterProxyModel(this);
proxyModel->setSourceModel(model);
yourTableWidget->setModel(proxyModel);
No guarantee in so far that I never tried to replace the default model in a QTableWidget. If possible you should look into the Qt views and models. Initially they look harder to use, but it pays to get comfortable with them. IMHO QTableWidget is just an ancient relict from Qt3.

Are you sure it didn't sort the data well? Make sure you do add number there, not string. So, to add 40 to QTableWidget row, you use this data:
36: {'firstname': 'b', 'lastname': '111', 'email': 'foo#gmail.com',
'affiliate': 'Stuart Little', 'total_account_value': 40},
Instead of this:
36: {'firstname': 'b', 'lastname': '111', 'email': 'foo#gmail.com',
'affiliate': 'Stuart Little', 'total_account_value': '40'},
QTableWidget will recognize it as integer and will sort it well

Related

QT Model/View Project doesnt show Strings from QList

My QTableView doesn't show strings from a QStringList.
In QTableWidget I have QTableWidgetItems. Must I set the strings manually or will the view show them automatically? In all the tutorials I don't see a "->setItem", they appear automatically.
I have 2 QLineEdits that give the QStrings to my Model :
void View::pushButtonClicked() {
meinModel->setData(txtname->text(), txtvalue->text());
}
In setData I push the Strings in two QLists.
names.push_back(name);
values.push_back(value);
I emit a dataChanged signal with the index from topleft and bottomright.
QModelIndex topLeft = createIndex(names.size()+1,0);
QModelIndex bottomRights = createIndex(names.size()-1,1);
emit dataChanged(topLeft, bottomRights);
I have a QAbstractTableModel and so i override the columnCount, rowCount and data Method.
In my data() Method I return my value and name:
QString returnValue;
if(0 == index.column()) { returnValue = names.at(index.row()); }
All of this compiles without warnings, but doesn't work correctly :( Is there something I'm doing obviously wrong?
One obvious problem is that you didn't get the semantics of dataChanged correctly. dataChanged means that an existing item has changed its value. When you change the structure of the model by adding/removing rows or columns, you have to enclose the modification in beginXxx and endXxx calls - see this answer for details.
For example:
void MyModel::setData(const QString & name, const QString & value) {
beginInsertRows(QModelIndex(), names.size(), names.size());
names.push_back(name);
values.push_back(value);
endInsertRows();
}

qt5 view different roles of model data

I want to use a QTableView to show different roles of an item, e.g. column 1 shows the DisplayRole data, column 2 shows UserRole, column 3 shows UserRole+1, etc
I've created this by making my own item delegate and assigning it to the appropriate column. However, to get access to the same item the delegates have to access their siblings. For example, here's my setEditorData function:
void UserRoleDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QModelIndex baseIndex = index.sibling(index.row(),0);
QVariant v = baseIndex.data(Qt::UserRole);
if(v.isValid())
static_cast<QLineEdit*>(editor)->setText(v.toString());
}
Right now, it's hardcoded that column 0 contains the real object and other columns just access that via the sibling function. I'm worried, though, that it's fragile in that changing the column order will break it.
There are obvious ways to manage that, but I'm just wondering if I'm taking the right approach. Is there a better option to display different aspects of the same item?
To avoid possible code rewrite you simple could use the enumeration for your column numbers. For example:
enum ColumnNumber {
Base,
Sub1,
Sub2
}
And in your delegate's code:
[..]
QModelIndex baseIndex = index.sibling(index.row(), Base);
Thus, if you need to change the column order, you simply need to change the order of your enum values. The rest of code will work correctly as it is.
WRT to item delgate usage - it looks rather strange. Such things used to made in the model class, especially in QAbstractItemModel::data(...) function, using it with Qt::EditRole role. For example:
QVariant Model::data(const QModelIndex &index, int role) const
{
if (role == Qt::EditRole) {
// Get the data that stored in the first column
// ..
return data;
}
[..]
}

Displaying a tooltip for QTreeWidgetItem when it's hovered without calling setTooltip() for every item

I want to display a tooltip for QTreeWidgetItem that's hovered. However, getting a tooltip is not a very fast process in my case, so I don't want to call setTooltip() for every single item. I want to do it on demand, on some event or signal. What's the easiest way to do it?
The best solution I've found is to subclass QTreeWidgetItem, override virtual QVariant data(int column, int role) const; and return a tooltip for this item when data is called for Qt::ToolTipRole.
I think that it should be easier to achieve what you want if you migrate to a QTreeView/Model pattern.
QAbstractItemModel has a role for tooltips: Qt::ToolTipRole
You could subclass a Model to reimplement the
QVariant QAbstractItemModel::data ( const QModelIndex & index, int role = Qt::DisplayRole ) const [pure virtual
method.
So, when receives a Qt::TooltipRole, it calculates/recovers from an internal cache.

Adding a 'None' option to a QComboBox linked to a model

I have a QComboBox so the user can a network name from from a model column. I'm using code like this:
self.networkSelectionCombo = QtGui.QComboBox()
self.networkSelectionCombo.setModel(self.model.worldLinks)
self.networkSelectionCombo.setModelColumn(WLM.NET_NAME)
I'm using PySide, but this is realy a Qt question. Answers using C++ are fine.
I need to give the user the option of not selecting any network. What I'd like to do is add an extra item to the combo box called 'None'. However this will just get overridden by the model contents.
The only way I can think of is to create an intermediate custom view on this model column and use that to update the combo, then the view can handle adding in the extra 'magic' item. Does anyone know a more elegant way of doing this?
One possible solution is to subclass the model you are using in order to add there the extra item. The implementation is straight forward. If you call your model MyModel then the subclass would look like this (C++ used):
class MyModelWithNoneEntry : public MyModel
{
public:
int rowCount() {return MyModel::rowCount()+1;}
int columnCount() {return MyModel::columnCOunt();}
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const
{
if (index.row() == 0)
{
// if we are at the desired column return the None item
if (index.column() == NET_NAME && role == Qt::DisplayRole)
return QVariant("None");
// otherwise a non valid QVariant
else
return QVariant();
}
// Return the parent's data
else
return MyModel::data(createIndex(index.row()-1,index.col()), role);
}
// parent and index should be defined as well but their implementation is straight
// forward
}
Now you can set this model to the combo box.

How to set filter option in QTableWidget

In my application I have QtableWidget displaying multiple rows , line edit to enter string and push button, Requirement says upon clicking on push button Same QTableWidget should show only those rows which are having string entered into line edit.
I thought of using QSortFilterProxy Model but QTableWidget is having setModel(...)method private so I am unable to use QSortFilterProxy Model in this case. Please let me know how to implement Filter option in QTable Widget
Using a sort/filter proxy is probably overkill for this anyway.
It's a matter of iterating through all of your QTableWidgetItem objects, determining if their text matches the filter and calling QTableView::setRowHidden() as needed.
For example:
QString filter = textEdit->text();
for( int i = 0; i < table->rowCount(); ++i )
{
bool match = false;
for( int j = 0; j < table->columnCount(); ++j )
{
QTableWidgetItem *item = table->item( i, j );
if( item->text().contains(filter) )
{
match = true;
break;
}
}
table->setRowHidden( i, !match );
}
I HIGHLY recommend going about this in the following way! This is how it is meant to be done in Qt.
Look at the tutorial on Qt Model/View Programming. The problem is that QTableWidget is a convenience class that hides the Model/View stuff for you. In your case, you can't (or shouldn't) ignore the Model/View structure Qt provides.
What you will need to do:
Use a QTableView instead of a QTableWidget.
Subclass QAbstractItemModel and implement data() (for reading), and all the other functions you need from the documentation. This is the trickiest part, but refer to the above link for a walkthrough of how to do this.
Create a QSortFilterProxyModel and setModel() of the QTableView to it.
setSourceModel() of your QSortFilterProxyModel to your subclassed model.
Set the string you want to filter on using setFilterFixedString() or setFilterRegExp() in your QSortFilterProxyModel
Let me know if this helps. This is far more professional, and in the long run, elegant, than iterating through all the elements in your table.

Resources