I am developing a Qt application that fetches and displays executions from the exchange. I am coloring rows that represent sells with red and buys with green. There is a requirement to have contiguous rows that represent same sides (e.g. buy or sell) to have slightly darker/lighter colors. I am using a custom model and overriding data(const QModelIndex &index, int role) function. Below is what handles coloring stuff:
...
const auto& execution = m_data[index.row()];
if(role == Qt::BackgroundRole) {
const auto side = execution.side_;
if(side == 'S') {
return (index.row() & 1) ? QVariant(light_red_) : QVariant(lighter_red_);
} else if(side == 'B') {
return (index.row() & 1) ? QVariant(light_green_) : QVariant(lighter_green_);
}
}
...
This is what the output looks like:
As you can see contiguous rows that belong to the same side sometimes have the same color. However, I would expect from my implementation to color lighter and darker in alternating rows. setAlternatingRowColors() is not suitable for me since I am coloring sides with different colors.
Edit: I am also using QSortFilterProxyModel and sorting view by a column in descending order.
Related
In a column in a QTableWidget I want to display some double values. By doing the following, I get my desired display:
double value = 1234.567;
QTableWidgetItem* qti = new QTableWidgetItem(QString::number(value , 'f', 4));
Now, if I enable sorting on the table and sort the values in this column, it will sort as strings. So 90.0000 will come be "larger" than 800.0000 for example (9 > 8).
If I do this instead:
QTableWidgetItem* qti = new QTableWidgetItem();
qti->setData(Qt::DisplayRole, value);
or
QTableWidgetItem* qti = new QTableWidgetItem(QString::number(value , 'f', 4));
qti->setData(Qt::DisplayRole, value);
I can sort my column correctly, but I "lose" the formmating (12.0000 is displayed as 12).
I've also tried like this:
QTableWidgetItem* qti = new QTableWidgetItem();
qti->setData(Qt::UserRole, value);
qti->setData(Qt::DisplayRole, QString::number(value, 'f', 4));
How can I format the display of the values, while still enabling sorting?
(In all code snippets above, the QTableWidgetItems' are added by:
table->setItem(rowNumber, colNumber, qti);
where table is a QTableWidget)
You do it wrong. Let's do it right. The solution is to subclass QTableWidgetItem.
class CustomTableWidgetItem : public QTableWidgetItem
{
public:
CustomTableWidgetItem(const QString txt = QString("0"))
:QTableWidgetItem(txt)
{
}
bool operator <(const QTableWidgetItem& other) const
{
qDebug() << "Sorting numbers";
return text().toFloat() < other.text().toFloat();
// text() is part of QTableWidgetItem, so you can write it as QTableWidgetItem::text().toFloat() as well
}
};
Example insertions
ui->tableWidget->setSortingEnabled(false);
ui->tableWidget->insertRow(0);
ui->tableWidget->setItem(0, 0, new CustomTableWidgetItem(QString::number(90.0005, 'f', 4)));
ui->tableWidget->insertRow(1);
ui->tableWidget->setItem(1, 0, new CustomTableWidgetItem(QString::number(800.0003, 'f', 4)));
ui->tableWidget->insertRow(2);
ui->tableWidget->setItem(2, 0, new CustomTableWidgetItem(QString::number(200.0010, 'f', 4)));
ui->tableWidget->insertRow(3);
ui->tableWidget->setItem(3, 0, new CustomTableWidgetItem(QString::number(200.0020, 'f', 4)));
ui->tableWidget->setSortingEnabled(true);
Sorting behaviour is as expected
ascending
90.0005
200.0010
200.0020
800.0003
descending
800.0003
200.0020
200.0010
90.0005
PS: Remember to turn off sorting before you insert bew items.
Another way of doing this is by using QStyledItemDelegate attached to the double column(s). It need not do much except implement the QStyledItemDelegate::displayText() function.
For further details, see QTableView and display of doubles at Qt Centre.
Here's what I came up with, based on Mangesh's answer, for a similar problem displaying a percentage:
class percentageItemC : public QStyledItemDelegate
{
QString displayText(const QVariant &value, const QLocale &locale) const
{
return QString::asprintf("%0.3lf%% ", value.toDouble());
}
} percentageItem;
That's any number of digits to the left of the decimal, 3 to the right, and a long float with a percent sign. Then, do a
ui->twMyTable->setItemDelegateForColumn(1, &percentageItem);
in yer form's class. I can also re-use them for other columns, and presumably rows.
This method doesn't slow things down with extra text to float conversions. You can also have nice extras, like commas, percent signs and currency signs in it. I have this for currency:
class currencyItemC : public QStyledItemDelegate
{
QString displayText(const QVariant &value, const QLocale &locale) const
{
return locale.toCurrencyString(value.toDouble()) + " ";
}
} currencyItem;
I use:
QTableWidgetItem *item = new QTableWidgetItem();
item->setData(Qt::EditRole, number);
to create the table items. It is smart about these: it creates a QVariant, and then sorts based on the type. This way, you don't have to set the underlying data to a string, but it can be any type it has a sort for.
The columns sort numerically like they're s'posed to, but they display in any format ya like. ;}
From my point of view the best option to solve this problem is to create new class with usage of setData() with Qt::UserRole to store unformatted data. In this setup you can store real data and text together. This has more memory consumption but you doesn't need to convert from string to int/float/etc.
I've tested many other roles Qt::EditRole, Qt::DisplayRole, etc and they are overwriting stored data/text and you can't store both values.
Here is my final solution of class displaying % percent values (python code)
from PyQt5 import QtWidgets, QtCore
class PercentTableWidgetItem(QtWidgets.QTableWidgetItem):
# Float value
value: float = 0
def __init__(self,
value: float = 0
):
''' Constructor.'''
super().__init__()
# Set variables
self.value = value
# Set item data
self.setData(QtCore.Qt.UserRole, value)
self.setText(f'{self.value:.2f}%')
def __lt__(self, other: QtWidgets.QTableWidgetItem):
''' Operation < for sorting.'''
value = other.data(QtCore.Qt.UserRole)
return (value is not None) and (self.value < value)
Usage in code as simple as below
table = QTableWidget()
table.setItem(0,0,PercentTableWidgetItem(35.67))
I have a grid that is clickable but I am unsure how to proceed with a certain set of rules.
Edit: I rewrote the rules in a more understandable fashion. Very similar to that of the game of life.
Setup
21 cells across / columns
10 cells down / rows
4 base cells vertically aligned in the centre of the board.
Outline cells will surround the base cells.
Every other cell begins as inactive.
Base Cells [2]
Constant and active blue cells in the middle, which cannot be removed.
Active [0] -> [1]
When clicked, an inactive white cell becomes black
if
the edge touches the edge of a base cell
or
the edge touches the edge of another active cell
(either to the left, right, top or bottom – not diagonally.)
else
remain inactive
Inactive [1] -> [0]
When clicked, an active black cell returns to white.
Outline [3]
A series of yellow cells that will constantly update to surround the neighborhood of active cells.
Could anyone help me in achieving this, I would appreciate comments to help me understand the process.
Here is my current code:
int boxsize = 100;
int cols, rows;
color[][] colors;
int saved_i = -1;
int saved_j = -1;
void setup() {
size(1300, 600);
cols = width/boxsize;
rows = height/boxsize;
colors = new color[cols][rows];
for (int i=0; i<cols; i++) {
for (int j=0; j<rows; j++) {
colors[i][j] = color(255);
}
}
}
void draw() {
background(255);
for (int i=0; i<cols; i++) {
for (int j=0; j<rows; j++) {
fill(colors[i][j]);
rect(i*boxsize, j*boxsize, boxsize, boxsize);
}
}
}
void mousePressed() {
for (int i=0; i<cols; i++) {
for (int j=0; j<rows; j++) {
int x = i*boxsize;
int y = j*boxsize;
if (mouseX > x && mouseX < (x + boxsize) && mouseY > y && mouseY < (y + boxsize)) {
if ( saved_i == -1 || saved_i == i || saved_j == j ) {
colors[i][j] = color(0);
if (j>0) colors[i][j-1]=color(255, 255, 0);
if (j>0) colors[i+1][j-1]=color(255, 255, 0);
if (j<rows-1) colors[i][j+1]=color(255, 255, 0);
if (j<rows-1) colors[i+1][j+1]=color(255, 255, 0);
if (i>0) colors[i-1][j]=color(255, 255, 0);
if (i>0) colors[i-1][j-1]=color(255, 255, 0);
if (i>0) colors[i-1][j+1]=color(255, 255, 0);
if (i<cols-1) colors[i+1][j]=color(255, 255, 0);
saved_i = i;
saved_j = j;
}
}
}
}
}
Your question is pretty broad, so I'll answer in broad terms. You need to figure out four things:
How to represent your cells. In other words, what type of variable you want to store your grid in. You're using colors now, but you probably don't want to do it that way. The way I see it, you have three logical options:
Use a 2D array of enum values. The enum would have states for BASE, ACTIVE, INACTIVE, and OUTLINE. This is probably the correct way to go.
Use a 2D array of ints. 0 for base, 1 for active, 2 for inactive, 3 for outline. Using an enum is probably better, but this is probably easier for a novice to understand.
Use a 2D array of objects. Create a class that represents a cell, and the object would store its state (in either an enum or an int). You would use this approach if you wanted other logic inside each cell, or maybe if you wanted each cell to keep track of its own neighbors.
How to change the state of a single cell on mouse click. You've got logic that deals with colors, now you just have to apply that logic to the data structure you choose in step 1. Maybe create a function that takes mouseX and mouseY and returns the position in the array at that location.
How to get the new state for each cell for the next generation. Create a function that takes the position of one cell (its row and column in the 2D array) and returns the state that the cell should have in the next generation. This is the "meat and potatoes" of your project, and separating it out will help you isolate the logic. Get out a piece of grid paper and draw some examples. If you know the position of a cell, what are the positions of its neighbors? There are a ton of tutorials on the Game of Life out there that will have this logic.
How to update your grid. Remember that you have to do step 2 to every cell in the grid before you update the whole grid. This means that you have to make a new 2D array each iteration.
Break your problem down down like this, and post a new question if you get stuck on a particular step. It's hard to help with general "how do I do this" type questions. It's much easier to help with more specific questions like "I tried X, expected Y, but got Z instead. What am I doing wrong?"
Good luck!
I have a map = std::map<std::string, myItemModel *>, where myItemModel inherits QAbstractItemModel.
I want now to combine all myItemModel in one single myItemModel (every other item model would be fine too).
So that there is one big myItemModel.
Is there a 'qt-way' to do this?
It can be done, but it's not trivial. It depends on your implementation of QAbstractItemModel and that's why it hasn't been done in Qt.
Here are steps to implement a model which is a collection of models:
Create a new class inherited from QAbstractItemModel
Add methods to add other models to that
Process all signals from child models which contains indexes (you'll need to change them, look #10)
Forward all signals which doesn't contain indexes.
Implement rowCount and provide a sum of all models rows.
Implement columnCount and provide a number of columns in your models.
Implement index, return createIndex(row, column, NULL);
Implement parent, return QModelIndex(); I hope your models are not trees
Implement data,setData etc. addressing calls to the right model. Use methods from #10 to convert indexes.
Create methods to convert a child model index to a base model index and back.
Example (indexes):
BaseModel ChildModel1 ChildModel2
0,0 0,0
1,0 1,0
2,0 0,0
3,0 1,0
4,0 2,0
p.s. Think about creating a cache of indexes mapping.
This is an example of a method to convert a base model index to a child model index:
const QModelIndex childModelIndex(const QModelIndex& baseModelIndex) const
{
if (!baseModelIndex.isValid())
{
return QModelIndex();
}
int count = 0;
const int row = baseModelIndex.row();
for (QList<QAbstractTableModel*>::const_iterator it = m_models.begin();
it != m_models.end(); it++)
{
const int currentCount = (*it)->rowCount();
if (row >= count && row < count + currentCount)
{
return (*it)->index(row - count, 0);
}
count += currentCount;
}
ASSERT(false);
return QModelIndex();
}
This is an example of a method to convert a child model index to a base model index:
QModelIndex baseModelIndex(const QModelIndex& childModelIndex) const
{
int row = childModelIndex.row();
for (QList<QAbstractTableModel*>::const_iterator it = m_models.begin();
it != m_models.end(); it++)
{
if (childModelIndex.model() == *it)
{
return index(row, ind.column());
}
row += (*it)->rowCount();
}
return QModelIndex();
}
The KDE Frameworks project contains a module called KItemModels, which includes a class called KConcatenateRowsProxyModel. It does exactly what you want. The library is released every month as part of the [KDE Frameworks releases], the code is continuously unit tested on https://build.kde.org. All this is licensed under LGPL v2 or later.
Subclassing QAbstractItemModel, I have implemented my own index() method as required. I currently check for valid inputs, but I'm wondering if that's correct. I'm wondering if it's ever valid to create an index for a non-existent piece of data? Perhaps while inserting a row or column?
Code:
QModelIndex LicenseDataModel::index(int row, int column, const QModelIndex & /*parent*/) const
{
/// TODO: Is this necessary? Should we avoid creating invalid indexes? Or could this
/// be a bug?
if (validRowColumn(row, column))
return createIndex(row, column);
return QModelIndex();
}
[ I have a better answer. :-) ]
Although I merely repeat what Giuseppe D'Angelo from KDAB has to say about this...
You must distinguish between an invalid and an ill-formed QModelIndex:
About invalid indices (from Navigation and model index creation in Qt's Model/View Programming guide):
QAbstractItemModel::parent(): Provides a model index corresponding to the parent of any given child item. If the model index specified corresponds to a top-level item in the model, or if there is no valid parent item in the model, the function must return an invalid model index, created with the empty QModelIndex() constructor.
This explains the meaning of index.isValid():
A valid index refers to an existing item, the invalid index refers to the root of all items.
Giuseppe D'Angelo first notes that an invalid index (with .isValid() returning false) is still a valid input to functions like rowCount(), hasChildren() etc.
But a QModelIndex can also be ill-formed. It can have a non-existing row or column index, and it can even be from a different model. And QModelIndex::isValid() does not check that.
Giuseppe D'Angelo says:
I personally maintain quite a strong point of view about this issue: passing such indices is a violation of the API contract. A model should never be assumed to be able to handle illegal indices. In other words, in my (not so humble) opinion, the QAbstractItemModel API has a narrow contract.
But since All Programmers Make Mistakes (TM), it facilitates the debugging process to check indices for being well-formed. For this purpose, Giuseppe D'Angelo introduced QAbstractItemModel::checkIndex() into Qt 5.11.
If you're still using a lower Qt version, you can simply write that function yourself.
[ If anyone has a better answer, I'll gladly accept it. ]
Looking at the source for QListWidget, it seems that checking the inputs is what Qt does itself:
QModelIndex QListModel::index(int row, int column, const QModelIndex &parent) const
{
if (hasIndex(row, column, parent))
return createIndex(row, column, items.at(row));
return QModelIndex();
}
It also appears that I didn't know about hasIndex() which will do what my validRowColumn() method does.
bool QAbstractItemModel::hasIndex(int row, int column, const QModelIndex &parent) const
{
if (row < 0 || column < 0)
return false;
return row < rowCount(parent) && column < columnCount(parent);
}
For that matter, I'm not sure why the documentation uses index.isValid() everywhere when hasIndex(index.row(), index.column(), index.parent()) would be more appropriate. Then, I'm sure a hasIndex(QModelIndex &) method would be added. hasIndex() does the same check as QModelIndex::isValid() and more:
inline bool isValid() const { return (r >= 0) && (c >= 0) && (m != 0); }
I am starting a project using Qt. I am trying two approaches to get a View to do some of the following. This question involves the approach of inheriting from QTreeView.
I like what QTreeView does. I just want some added features.
First, what I want is to make a hierarchy tree view that will allow me to see categories containing other categories, the further right the columns go the more specific they are until it gets to the most specific. The metrics are shown on the row containing the most specific column. The view rows containing each generalized column will be bold and contain a summary of each metric, calculated by the model (or view?). The metrics will be in the model on each row in terms of the most specific column.
For example, consider a model with the following data (the last 3 columns containing numbers):
Country|Province-State|County-Parish|City-Town|Population|PerCapitaIncome|WalMarts
So my view would look similar to this:
Country Province-State County-Parish City-Town Population PerCapitaIncome Walmarts
+ USA 250000000 42000 2354
+ Alabama 9000000 23000 153
+ Barbour 15324 19032 1
Eufaula 6520 23000 1
+ Tennessee 14000000 29000 299
+ Hamilton 70000 41000 4
East Ridge 23000 32000 2
Second, I need it to work with QSqlTableModel. I have seen it show the model before, but it doesn't have any way to create the rows by a heirarchy, similar to above. That was going to be my second modification.
The third reason is bold headers are only an option if you have the sort turned on via:
view->setSortingEnabled(true);
When sort is on, the bold headers only works for the higher-up rows and then turns off on lower ones. I want to fix that bug.
The QTreeView::drawRow virtual method looks to be all I need to override to accomplish the first challenge (and perhaps the third). The second challenge dealing with QSqlTableModel, I'm not so sure about.
Anyhow, I built a simple class inheriting from QTreeView with a generic ctor and dtor that just calls the QTreeView methods. As for drawRow, however, I ran into the following problem. The QTreeView::drawRow function starts out like this:
QTreeView::drawRow(
QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const {
Q_D(const QTreeView);
QStyleOptionViewItemV4 opt = option;
const QPoint offset = d->scrollDelayOffset;
const int y = option.rect.y() + offset.y();
const QModelIndex parent = index.parent();
const QHeaderView *header = d->header;
const QModelIndex current = currentIndex();
const QModelIndex hover = d->hover;
const bool reverse = isRightToLeft();
const QStyle::State state = opt.state;
const bool spanning = d->spanning;
const int left = (spanning ? header->visualIndex(0) : d->leftAndRight.first);
const int right = (spanning ? header->visualIndex(0) : d->leftAndRight.second);
...
The function relies on have Q_D(const QTreeView) run succesfully and return "d", an instance of the QTreeViewPrivate class which contains important info related to layout and the remainder of the function. Since I am inheriting into my CustomTreeView class, CustomTreeViewPrivate will have to be defined and instantiated first if I am to run Q_D(const QTreeView) in CustomTreeView::drawRow().
Is creating this private class really necessary to inherit and make significant changes? What is the benefit of inheriting here if all I can do is some perfunctory processes and then call the QTreeView::drawRow to do the actual drawing?
I want to change how its drawing.
I will try to cover as much of your question as possible. The big thing throughout all of your problems is that much of what you are trying to accomplish should be done through the model, not the view (such as having certain entries in bold). Because of this, you will have to make your own model. You can inherit a QSqlTableModel and alter things as you wish. For example, if you want to bold certain items, in the data model, you could write
QVariant MyModel::data(const QModelIndex & index, int role) const
{
QVariant result = QSqlTableModel::data(index, role);
// add your own qualifications to the following if statement, checking the row
// and such
if(role == Qt::FontRole) {
QFont font = result.value<QFont>();
font.setBold(true);
return font;
}
return result;
}
The last thing you wrote was about Q_D. This is only for use in the Qt source code. If you are implementing your own paint function, you do not have to use this macro.
I would read up on models very heavily, you may need a lot of the stuff in the documentation.