PDA

View Full Version : A few queries about Model View Programming



montylee
12th December 2008, 00:44
I have a database consisting of a table with some fields. I am using QSqlTableModel to get the data from the database and displaying it in a QTableView. Now, the database has 3 fields i.e. tracks, artist and time. All of the data in these fields is coming from the model and being displayed properly.
I have added another column to the QTableView consisting of QCheckBox for each row (See attached image). The QCheckBox is not part of the database or model. I am using the following code for attaching QCheckBox to each row:


void MyClass::createCheckItems(QTableView *table)
{
for (int i = 0; i < m_pModel->rowCount(); ++i) {
QCheckBox *cbox = new QCheckBox(QObject::tr (""));
// Enable autofill of the widget background to hide the model data
cbox->setAutoFillBackground(TRUE);
// Set up slots
connect(cbox, SIGNAL(stateChanged(int)), this, SLOT(checkBoxClicked(int)));
// Add checkboxes in the 4th column of QTableView
table->setIndexWidget(m_pModel->index(i, 3), cbox);
}
}

I am able to retrieve the current row and get the track name when the user clicks on a check box.

My queries:
1) I am using setIndexWidget() to display checkboxes in the 4th column and my code is working fine. Is it ok or should i use QItemDelegate to add checboxes?
2) Currently the QTableView displays data directly received from the model (database). But suppose, i want to format the data received from the model before displaying on the QTableView, how can i do it? For e.g. suppose the "time" field is stored as seconds in the database and i want to display it in "minutes:seconds" in the QTableView, how can i do that?
3) I want to do custom drawing in the "tracks" field, for e.g. the currently playing track might have an icon next to the track name. How to do that?

Thanks in advance!

calmspeaker
12th December 2008, 03:25
1) I am using setIndexWidget() to display checkboxes in the 4th column and my code is working fine. Is it ok or should i use QItemDelegate to add checboxes?

According to my experience with the model/view,If you use delegate, you would change the model either.


2) Currently the QTableView displays data directly received from the model (database). But suppose, i want to format the data received from the model before displaying on the QTableView, how can i do it? For e.g. suppose the "time" field is stored as seconds in the database and i want to display it in "minutes:seconds" in the QTableView, how can i do that?

you can write the data() function in the model like this

if (role == Qt::DisplayRole)
{
if(index.column() == column_time)
{
//seconds is the "time" field stored value
int min = seconds/60;
int sec = seconds%60;
return QString(%1:%2).arg(min).arg(sec);
}
//.......
}



3) I want to do custom drawing in the "tracks" field, for e.g. the currently playing track might have an icon next to the track name. How to do that?

also in the data() func
Make a QIcon,return it when role == Qt:: DecorationRole

wysota
12th December 2008, 07:55
1) I am using setIndexWidget() to display checkboxes in the 4th column and my code is working fine. Is it ok or should i use QItemDelegate to add checboxes?
Avoid using index widgets. Use a Qt::CheckStateRole of the model instead - either directly in your model or through a proxy model if you can't modify the base model.


2) Currently the QTableView displays data directly received from the model (database). But suppose, i want to format the data received from the model before displaying on the QTableView, how can i do it? For e.g. suppose the "time" field is stored as seconds in the database and i want to display it in "minutes:seconds" in the QTableView, how can i do that?
Provide a custom delegate or use a proxy model that will return different data. If you can change the base model, you might want to either use the solution already posted or introduce a custom role that would return data formatted the way you like it and use a custom delegate to fetch it from the model.


3) I want to do custom drawing in the "tracks" field, for e.g. the currently playing track might have an icon next to the track name. How to do that?

Either return the decoration as the Qt::DecorationRole of the model or use a proxy model or a custom delegate.

montylee
12th December 2008, 17:29
According to my experience with the model/view,If you use delegate, you would change the model either.
Sorry, i didn't get what you exactly mean.


you can write the data() function in the model like this

if (role == Qt::DisplayRole)
{
if(index.column() == column_time)
{
//seconds is the "time" field stored value
int min = seconds/60;
int sec = seconds%60;
return QString(%1:%2).arg(min).arg(sec);
}
//.......
}

I haven't used Roles till now and i am new to the MVC architecture. So, can you please tell me how exactly i can use the above function? As far as i understand, i have to subclass the default QSqlTableModel and rewrite the data() function as you have given above. Is it correct? Will the model automatically pick up the formatted data when i use model->setTable(tableName)? Since i am not using data() function directly to fetch the data from the model when the application starts up, the data is automatically filled up as soon as i set the QTableView to use the model. So, just wanted to know if rewriting the data() function will force the model to display formatted data by default.


also in the data() func
Make a QIcon,return it when role == Qt:: DecorationRole
I got what you mean but i am not able to understand how to use it. Suppose i want to display an icon with the song name field. The currently playing track should have an icon next to the track name in addition to the track name. So, is the following code correct:


if(index.column() == column_track)
{
if (role == Qt::DecorationRole)
{
QIcon *icon = new QIcon(this);
// Attach image to icon
return QIcon;
}
if (role == Qt::DisplayRole)
{
return index.data();
}
//.......
}
So, in the above code, i am returning an icon for the tracks column for DecorationRole and returning the track name for DisplayRole. Will index.data() return the track name or do i need to use some other API?
Will the above code display both the track name and icon when the application starts up?

One more doubt (same doubt as above): The above code will work if i call the data() explicitly but will the proper data be displayed when the application first starts up?


Avoid using index widgets. Use a Qt::CheckStateRole of the model instead - either directly in your model or through a proxy model if you can't modify the base model.
I was thinking about using the CheckStateRole. Please elaborate on the usage of the same. As i mentioned the column containing checkbox is not in the database. I get only the 1st 3 columns from the database and then add checkboxes to the 4th column, The checkboxes are meant for adding songs to a playlist and have nothing to do with the song database.
So, if i use the following code in the data() function of the model, will it work?


if(index.column() == 3) // 4th column for checkboxes
{
if (role == Qt::DisplayRole)
{
QCheckBox *cbox;
return cbox;
}
}
//.......
}
Is the above code correct? i am not sure about it. In QTableWidget, we can just set the checkstate property to get a checkbox but in QTableView, i am not sure if i have to create a QCheckBox or not.
And how will i get the events for the checkbox? Basically i want to get the row number or track name when user clicks on a checkbox. So, whenever user selects/deselects a checkbox, i need to get the track name and add/delete it from the playlist.

Also, since from the database i'll get only 3 columns, how will i add another column to the model without disturbing the database? I am using model->insertColumn() in the code which i posted in my first post.



Provide a custom delegate or use a proxy model that will return different data. If you can change the base model, you might want to either use the solution already posted or introduce a custom role that would return data formatted the way you like it and use a custom delegate to fetch it from the model.
I am not familiar with delegates, so the solution posted by @calmspeaker looks ok to me. I don't need to modify the database from the GUI, so i can avoid using the delegates.



Either return the decoration as the Qt::DecorationRole of the model or use a proxy model or a custom delegate.
Again, can i use the solution posted by @calmspeaker?

Thanks a lot for helping me out! I am really new with MVC and it's a bit complex for newbies.

wysota
13th December 2008, 17:10
I was thinking about using the CheckStateRole. Please elaborate on the usage of the same. As i mentioned the column containing checkbox is not in the database. I get only the 1st 3 columns from the database and then add checkboxes to the 4th column, The checkboxes are meant for adding songs to a playlist and have nothing to do with the song database.
You don't have to put them in a separate column. Place them alongside the actual data in column 0. For this to work you need to reimplement QAbstractItemModel::flags() to return ItemIsUserCheckable among other flags for indexes having column()==0. Then you need to return true or false from the data() method of the model for the CheckStateRole and allow the model to be editable by allowing setData() to change the CheckStateRole of your model. The rest will be taken care of automatically.


So, if i use the following code in the data() function of the model, will it work?


if(index.column() == 3) // 4th column for checkboxes
{
if (role == Qt::DisplayRole)
{
QCheckBox *cbox;
return cbox;
}
}
//.......
}
No, this is wrong. You don't return widgets from the model.


i am not sure about it. In QTableWidget, we can just set the checkstate property to get a checkbox but in QTableView, i am not sure if i have to create a QCheckBox or not.
It's the same here - you need to set appropriate attributes. There is no real checkbox anywhere, it is mimicked by the item delegate.


And how will i get the events for the checkbox?
You don't. There is no need to.


Basically i want to get the row number or track name when user clicks on a checkbox. So, whenever user selects/deselects a checkbox, i need to get the track name and add/delete it from the playlist.
Connect to the dataChanged() signal of the model and check the CheckStateRole of the appropriate index - it will either be true or false depending on the state of the "checkbox" (remember there is no real checkbox there although you see one).


Also, since from the database i'll get only 3 columns, how will i add another column to the model without disturbing the database? I am using model->insertColumn() in the code which i posted in my first post.
You won't - you will display the checkbox in the first column. It looks better this way.


I am not familiar with delegates, so the solution posted by @calmspeaker looks ok to me. I don't need to modify the database from the GUI, so i can avoid using the delegates.
You already are using delegates. It's only a matter of how much control you want to have over the mechanism of displaying your data in views. The standard delegate takes the DisplayRole and renders it to the target widget with DecorationRole used as the icon. If you want something else, you subclass the default delegate and extend/substitute the painting routine.


Again, can i use the solution posted by @calmspeaker?
There is more than one solution for most problems. Choose the one you are most comfortable with.

montylee
15th December 2008, 16:39
hey thanks a lot for answering my queries!
Actual i can't change the location of the checkbox as it has already been defined as to be displayed in a separate column after the song time column.


You don't have to put them in a separate column. Place them alongside the actual data in column 0. For this to work you need to reimplement QAbstractItemModel::flags() to return ItemIsUserCheckable among other flags for indexes having column()==0. Then you need to return true or false from the data() method of the model for the CheckStateRole and allow the model to be editable by allowing setData() to change the CheckStateRole of your model. The rest will be taken care of automatically.
As per my requirement, i am thinking of first inserting a new column which is not in the database so it's not part of the mode initially. So, i'll use insertColumn() to insert the 4th column. Then i'll reimplement the flags() function and return ItemIsUserCheckable for the 4th column as you suggested.




So, if i use the following code in the data() function of the model, will it work?
Qt Code:

1.
if(index.column() == 3) // 4th column for checkboxes
2.
{
3.
if (role == Qt::DisplayRole)
4.
{
5.
QCheckBox *cbox;
6.
return cbox;
7.
}
8.
}
9.
//.......
10.
}

No, this is wrong. You don't return widgets from the model.

Here, i'll return just true and false based on the CheckStateRole.

Just wanted to know more about displaying an icon along with an image in the same field of the model. Suppose i want to display an icon with the song name field. The currently playing track should have an icon next to the track name in addition to the track name. So, is the following code correct:
Qt Code:

if(index.column() == column_track)
{
if (role == Qt::DecorationRole)
{
QIcon *icon = new QIcon(this);
// Attach image to icon
return QIcon;
}
if (role == Qt::DisplayRole)
{
return index.data();
}
//.......
}

So, in the above code, i am returning an icon for the tracks column for DecorationRole and returning the track name for DisplayRole. Will index.data() return the track name or do i need to use some other API?
Will the above code display both the track name and icon when the application starts up? If i call the data() function explicitly with DecorationRole i'll get a QIcon, so do i need to set the icon in the QTableView column explicitly? And i think you mentioned that i can't return widgets from the model, so how do i display an icon next to the text?

One more query:
For rewriting the data() and flags() functions to get the desired functionality i am thinking of subclassing the QSqlTableModel or QSqlQueryModel, will it work or do i have to subclass from QAbstractItemModel? I want to subclass from QSqlTableModel or QSqlQueryModel as i require the database functionality being provided with these classes.

calmspeaker
16th December 2008, 03:34
One way to know which type could be returned by data() function is , whether the data type could be converted to QVariant,;)
The best way to study MVC in qt is read the qt doc Model/View Programming.You could easily find it in the Qt assistant.
Wish you have fun in programming in Qt!

wysota
16th December 2008, 08:26
A simple rule says - never return a pointer from data(). Another simple rule says - take a working application that does what you want and look how it does it. A third rule says - Qt examples and demos are nice working applications :)

muellerp
16th December 2008, 08:53
Just a side question to "A simple rule says - never return a pointer from data(). ".

How would I return a QList?
AFAIK the data() returns a copy of the QList. This would be in my case very sad.

Or is the copy not a deep copy as it is implicitely shared, so using data with QList (or similar bigger objects) is not that harmful to speed and memory consumption?

Docu to QVariant says it has a speed and memory issue.

montylee
16th December 2008, 17:05
A simple rule says - never return a pointer from data(). Another simple rule says - take a working application that does what you want and look how it does it. A third rule says - Qt examples and demos are nice working applications :)
ya, actually i have read the Qt MVC tutorial from Qt Docs. I also looked at some examples.
Can you please answer my query regarding displaying an icon along with the text in the same field of the QTableView item?

One more query:
The data i want to display is coming as follows from the database:

struct myData {
QStringList trackName;
QStringList artistName;
QStringList time;
}

trackName string list contains all rows of the tracks fetched from the database, similarly artistName and time contains all rows of artist names and times corresponding to track names.

Now, which subclassing which model class should be suitable for me considering that i'll be displaying the data in a QTableView?

montylee
16th December 2008, 21:58
Ok i studied the AddressBook example in Qt Item Views examples directory. I think i can subclass QAbstractTableModel to create my own model by rewriting the necessary functions.


struct myData {
QStringList trackName;
QStringList artistName;
QStringList time;
}

trackName string list contains all rows of the tracks fetched from the database, similarly artistName and time contains all rows of artist names and times corresponding to track names.

Now, since i am getting the data from the database column vise, i would need to get individual items from the QStringList and put that in another list. This way i have to create a new list for each row, so that i can add it to a row in QTableView.

In the Address Book example, a list of QPair is used to maintain data for each row (since it has only 2 columns):
QList< QPair<QString, QString> > listOfPairs;

but since i have more than 2 columns, i am thinking of using a QList of QStringList i.e.

QList<QStringList> myList;

So, each entry in myList would correspond to one row in the model or QTableView. Then i can just add the data in QTableView as done in the Address Book example.

Please comment if the above approach will work or not.

One problem:
I was able to get a checkbox in the 3rd column by modifying the data() and flags() functions of the Address Book example. But i can only view the checkbox, i am not able to edit it. I am not sure what code to write in setData() function, so please help me.

montylee
17th December 2008, 01:14
I managed to display the data along with the checkbox in the QTableView properly using the QList<QStringList> approach i mentioned.

Now, the only problem is that i am not able to edit (check/uncheck) the checkbox. The setData function looks like this:



bool TableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.isValid() && role == Qt::EditRole) {
// Do something
} else {
if (index.isValid() && role == Qt::CheckStateRole) {
emit(dataChanged(index, index));
return true;
}
}

I am not sure what to write in the else condition for Qt::CheckStateRole.

The data() function has the following code:


if (role == Qt::CheckStateRole) {
if (index.column() == 3)
return (index.model()->data(index).toInt() != 0) ? Qt::Checked : Qt::Unchecked;

I think data() function is fine as i am able to view the checkbox.

Please help...

montylee
17th December 2008, 17:05
somebody please help! I just need to make the checkboxes editable now.

Edit: Nevermind, i got it working. Here's how i did it:

Defined a private variable in the model header file to store status of all checkboxes:


QList<bool> m_checkedStates;

data() function:

if (role == Qt::CheckStateRole && index.column() == 3) {
if (m_checkedStates[index.row()] == true)
return Qt::Checked;
else
return Qt::Unchecked;

setData() function:

if (role == Qt::CheckStateRole && index.column() == 3) {
m_checkedStates[index.row()] = !m_checkedStates[index.row()];
emit dataChanged(index, index);
return true;
}

Thanks a lot everyone for helping me out!

One final query:
If i want to draw the checkbox my self using QPainter, how to do it in this code?

wysota
17th December 2008, 18:48
Just a side question to "A simple rule says - never return a pointer from data(). ".

How would I return a QList?
You can return a QVariantList which is a QList of QVariant.

AFAIK the data() returns a copy of the QList. This would be in my case very sad.
Why so?


Or is the copy not a deep copy as it is implicitely shared, so using data with QList (or similar bigger objects) is not that harmful to speed and memory consumption?
The copy is always shallow as long as you don't modify it.


Can you please answer my query regarding displaying an icon along with the text in the same field of the QTableView item?
Haven't you seen any example that does that? :) In general you must be able to return data for both roles - QString for DisplayRole and QIcon or QPixmap for DecorationRole.


Now, which subclassing which model class should be suitable for me considering that i'll be displaying the data in a QTableView?
If you don't know which model to use, QStandardItemModel is almost always a good choice. If you are using a database though, you might want to use QSqlQueryModel or one of its descendants.



but since i have more than 2 columns, i am thinking of using a QList of QStringList i.e.

QList<QStringList> myList;
Bad choice.

QList<MyStruct> is much better.



I was able to get a checkbox in the 3rd column by modifying the data() and flags() functions of the Address Book example. But i can only view the checkbox, i am not able to edit it. I am not sure what code to write in setData() function, so please help me.
setData() must be able to set the CheckStateRole on the index and flags() must return ItemIsUserCheckable.


If i want to draw the checkbox my self using QPainter, how to do it in this code?

By setting a custom delegate and using QStyle::drawPrimitive() and PE_IndicatorCheckBox.

montylee
17th December 2008, 21:44
Haven't you seen any example that does that? :) In general you must be able to return data for both roles - QString for DisplayRole and QIcon or QPixmap for DecorationRole.
I'll try searching the Qt example for the icon thing. I managed to get the icon along with the song name. Here's the code:


if (role == Qt::DecorationRole && index.column() == 0) {
QPixmap pixmap ("icon.png");
return pixmap;
}
So, in the end it was pretty simple to get the icon.



If you don't know which model to use, QStandardItemModel is almost always a good choice. If you are using a database though, you might want to use QSqlQueryModel or one of its descendants.
I have decided to use QAbstractTableModel as it fits my requirements perfectly.



Bad choice.

QList<MyStruct> is much better.

Actually the data from the database is already coming as a QStringList so i have to use a QList of QStringList, so i have no choice here.



By setting a custom delegate and using QStyle::drawPrimitive() and PE_IndicatorCheckBox.
But using custom delegate i think the custom drawing will only be visible to the user when it goes into edit mode. I want the custom drawing to be visible at all times. Currently the normal checkbox (using QCheckBoxRole) is visible at all times, so i just want to add custom drawing to it. But i am not sure how to draw it as it's not a real checkbox, so a normal custom delegate might not work.

wysota
17th December 2008, 22:51
But using custom delegate i think the custom drawing will only be visible to the user when it goes into edit mode.
No, that's incorrect. Everything you see in the table is drawn by the delegate. Take a look at QTableView source code, there is no code related to rendering items.


Currently the normal checkbox is visible at all times, so i just want to add custom drawing to it. But i am not sure how to draw it as it's not a real checkbox.

It's not a real checkbox. It's drawn by the delegate. Here is the code it uses:


void QItemDelegate::drawCheck(QPainter *painter,
const QStyleOptionViewItem &option,
const QRect &rect, Qt::CheckState state) const
{
Q_D(const QItemDelegate);
if (!rect.isValid())
return;

QStyleOptionViewItem opt(option);
opt.rect = rect;
opt.state = opt.state & ~QStyle::State_HasFocus;

switch (state) {
case Qt::Unchecked:
opt.state |= QStyle::State_Off;
break;
case Qt::PartiallyChecked:
opt.state |= QStyle::State_NoChange;
break;
case Qt::Checked:
opt.state |= QStyle::State_On;
break;
}

const QWidget *widget = d->widget(option);
QStyle *style = widget ? widget->style() : QApplication::style();
style->drawPrimitive(QStyle::PE_IndicatorViewItemCheck, &opt, painter, widget);
}

montylee
18th December 2008, 00:10
Ok ,so it seems that it is possible to draw a custom checkbox which is visible at all times using a custom delegate.
But i am still confused as to how to implement a custom painted checkbox which will be visible at all times. My current code has the normal checkbox working using Qt::CheckStateRole as you suggested earlier. Now, if i want to add custom drawing to it, what should i do?

Suppose, i write a custom delegate to display a checkbox at all times. So, i'll rewrite the paint function of the delegate to display a virtual checkbox at all times. In addition, to support editing of the checkbox (check/uncheck) by the user, i need to rewrite the createEditor(), setEditorData() and setModelData() functions in the custom delegate.

So, when the application runs, user will see a virtual checkbox because of the paint() function of the custom delegate and if the user clicks the checkbox, the delegate should go into edit mode and have a real checkbox.

Is the above understanding correct? If i implement the above stuff, i would have to remove the Qt::CheckStateRole code as it will no longer be used.

I am still confused...

wysota
18th December 2008, 00:42
Ok ,so it seems that it is possible to draw a custom checkbox which is visible at all times using a custom delegate.
Honestly I don't see a point of doing that if the default delegate already does it.


But i am still confused as to how to implement a custom painted checkbox which will be visible at all times.
The code is in my previous post. It's taken directly from Qt sources.


My current code has the normal checkbox working using Qt::CheckStateRole as you suggested earlier. Now, if i want to add custom drawing to it, what should i do?
It's not a "normal" checkbox. It uses the code from my previous post.


Suppose, i write a custom delegate to display a checkbox at all times. So, i'll rewrite the paint function of the delegate to display a virtual checkbox at all times. In addition, to support editing of the checkbox (check/uncheck) by the user, i need to rewrite the createEditor(), setEditorData() and setModelData() functions in the custom delegate.

Could you first say what is the effect you want to achieve that is not available by default?


So, when the application runs, user will see a virtual checkbox because of the paint() function of the custom delegate and if the user clicks the checkbox, the delegate should go into edit mode and have a real checkbox.
Why would you want a real checkbox? What's wrong with the one the CheckStateRole provides?

montylee
18th December 2008, 00:56
Honestly I don't see a point of doing that if the default delegate already does it.

The code is in my previous post. It's taken directly from Qt sources.

It's not a "normal" checkbox. It uses the code from my previous post.

So you are saying that Qt::CheckStateRole actually uses the code which you pasted. So, basically it is just custom painting the checkbox.

Now, since i am already using Qt::CheckStateRole to get the checkbox, i need to add custom painting to it.



Could you first say what is the effect you want to achieve that is not available by default?


Why would you want a real checkbox? What's wrong with the one the CheckStateRole provides?
As i already mentioned. I just want a checkbox which is visible at all times and user can edit it. I have already implemented this functionality using Qt::CheckStateRole as you can see from the attached image. So, i am happy with what Qt::CheckStateRole has provided. Now, i just want to add custom painting to the visible checkboxes.

wysota
18th December 2008, 01:26
You can do that either by using style sheets or by subclassing one of the available delegate classes and reimplementing the way the checkbox is drawn.

montylee
18th December 2008, 17:35
You can do that either by using style sheets or by subclassing one of the available delegate classes and reimplementing the way the checkbox is drawn.
if i use a delegate class for drawing the checkbox, can i continue using Qt::CheckStateRole for displaying the checkbox and handle only drawing in the custom delegate?

wysota
18th December 2008, 20:52
You already are using a delegate to draw the checkbox. You just need to subclass it and draw something different instead of what the default delegate draws, for example by reimplementing QItemDelegate::drawCheck().

montylee
18th December 2008, 21:09
You already are using a delegate to draw the checkbox. You just need to subclass it and draw something different instead of what the default delegate draws, for example by reimplementing QItemDelegate::drawCheck().
Hmmm...that looks cool. Thanks a lot for helping me out in MVC. I am now beginning to get a hang of it.

Thanks again!!!

montylee
23rd December 2008, 22:25
I am back with another doubt :)
I want to add a new row (this should be the 1st row in th table) in my table view with a push button or a label. The label or push button will be named "ALL" and i need to trap the click event on the label/button.
Basically the "ALL" button/label will be used for checking/unchecking all checkboxes at once. So, if user wants to click all checkboxes in the 4th column, he'll click on the "ALL" button in the 4th column to check all checkboxes.

Now, is it possible to add push button or a label in the table model? i used the following code to add one additional row for the push button/label in the table:


int TableModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return m_tableData.size() + 1;
}

So, i have added 1 to the number of rows to add one extra row. But the problem is that the new row is added to the bottom of the table i.e. after all model data. I want to add it to the top of the table.

Secondly, i think i can't return a widget pointer from data() function of the model, so i tried creating a label using the following code in data() function:


if (role == Qt::DisplayRole && index.row() >= m_tableData.size() && index.column() == 3) {
return QVariant("ALL");
}

This shows a label named "ALL" in the 4th column and last row of the table. Now, how can i trap the click of this and select all checkboxes?

Edit: It seems i can trap the click of the label by trapping click() signal of QAbstractItemView() so it shouldn't be a problem.

But how can i make the label/push button appear on the 1st row instead of the last row? and for selecting all checkboxes, i can use setData() function and pass the value (check or uncheck) in the 2nd argument. Is it correct?

montylee
25th December 2008, 22:10
bump.
Anybody knows a solution to my problem?

wysota
30th December 2008, 10:18
Adding a button to the model doesn't make sense, this would break the logic-presentation separation. What you want is to modify the view. The usual way to do what you want would be simply to make it so that if you click on the header in the appropriate column (the one with checkboxes) all checks will go on or off. But if you want to do it "your way", you can use QAbstractItemView::setIndexWidget() for instance or you can use QAbstractScrollArea::setViewportMargins() to move the viewport down and place the button in the extra space between the header and the viewport.

Of course you might also make the header checkable as in the image below.

montylee
1st January 2009, 05:01
thanks! Actually i have placed the button to select all checkbox outside the QTableView for now. Based on the requirement i might be required to place it inside QTableView but still instead of the button i'll simply use a clickable label or icon for the same.

montylee
12th January 2009, 20:34
You already are using a delegate to draw the checkbox. You just need to subclass it and draw something different instead of what the default delegate draws, for example by reimplementing QItemDelegate::drawCheck().
ah, i am still working on the checkbox thing. I am able to display a custom checkbox image by subclassing QItemDelegate::drawCheck(). The drawCheck code is as under:


void CheckboxDelegate::drawCheck(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect, Qt::CheckState state) const
{
QBrush brush(QColor(19, 19, 70));
QRect r = rect.adjusted(0, -5, 50, 5);
painter->fillRect(r, brush);
r = rect.adjusted(20, -5, 25, 0);
if (state == Qt::Checked)
painter->drawPixmap(r, QPixmap("check-icon.png"));
/*else
painter->drawPixmap(r, QPixmap());*/
}

So, when i select the checkbox, my custom image is displayed. Now, i have some problems:

1) When the QTableView row is highlighted, the row highlight color is drawn over my checkbox and the checkbox doesn't show up on the selected row.
2) How can i give a different checkbox image for the case when a row is highligted? Basically i want to show 2 different checkbox images for highlighted and un-highlighted rows. But since drawCheck() doesn't have QModelIndex as argument, i am not able to find the model index for which drawCheck() is being called. If i can find the model index, i can compare it with the current highlighted row and show a different image for that row.
3) If i don't use this custom delegate, i get a checkbox in the 3rd column properly but the checkbox is displayed to the extreme left (see attached normal_checkbox.png). If i use my custom checkbox (see attached custom_checkbox.png), i see the checkbox properly but if i click on it, it doesn't toggle the checbox. Actual toggle is done when i click on the extreme left on the cell, so inspite of drawing a custom checkbox, the actual checkbox is to the left. Ideally, the toggle should be done, if user clicks anywhere in the cell.

In addition, as you can see from custom_checkbox.png, when a row is selected, the checkbox is not shown. Instead a blue area depicting the actual checkbox is shown.

wysota
12th January 2009, 22:58
Have you tried using stylesheets instead of doing everything manually? Maybe they would give enough control for you.

montylee
13th January 2009, 00:18
no, i am not using style sheets for now. I can't use style sheets as it's not in the project scope and schedule for now.

Please suggest something about my queries...

wysota
13th January 2009, 08:46
If you are to do what you are currently doing then it certainly is in scope. You are trying to change the looks of the application, that's exactly what they are for.

montylee
13th January 2009, 19:03
ya, actually i want to use style sheets but as i mentioned the schedule of project is tight and i can't switch to style sheets as of now. Currently i read all widget coordinates, color values and other stuff from a text file. We need to migrate from text file to CSS but it would most probably be in the next version of the application.

As of now, i have to stick to reading information from a text file and displaying widgets according to the information in the text file.

Please go thru my queries and if you know answer to any of them, it would be of great help to me.

wysota
13th January 2009, 19:57
We need to migrate from text file to CSS but it would most probably be in the next version of the application.
You can read settings from a text file and apply them using stylesheets.


Please go thru my queries and if you know answer to any of them, it would be of great help to me.

From what I see you are doing it the wrong way. You can't modify the rect you paint on. This is the rect that Qt gives you to draw the check on and it supplies the option object to read and apply parameters of the check drawn. If you want something different then provide a full-blown delegate derived from QAbstractItemDelegate. Currently you are strained by the classes you use and instead of getting rid of those constraints you try to work around them, this is not the way.

montylee
13th January 2009, 23:19
You can read settings from a text file and apply them using stylesheets.
hmmm maybe. i'll explore about this. So, if i use CSS can i customize everything i have mentioned in my queries?



From what I see you are doing it the wrong way. You can't modify the rect you paint on. This is the rect that Qt gives you to draw the check on and it supplies the option object to read and apply parameters of the check drawn. If you want something different then provide a full-blown delegate derived from QAbstractItemDelegate. Currently you are strained by the classes you use and instead of getting rid of those constraints you try to work around them, this is not the way.
okie, so i should not modify the rect argument in the drawCheck() function.
I suppose, if i subclass QAbstractItemDelegate then i can fully customize the checkbox look.

One query:
I have basically no idea about QPainter yet, so does the QPainter code i shared earlier (in void CheckboxDelegate::drawCheck) look ok (except modifying the rect offcourse)? I mean if i subclass QAbstractItemDelegate and rewrite the paint function. In the paint function, i'll just display a checkbox pixmap. In the paint() function, i'll get the information about the current model index too:

virtual void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const = 0
so i can display different images when a row is highlighted.

Now, in this case, can i specify my own rect and draw the checkbox image as i like? You said that in QItemDelegate:drawCheck(), i can't modify the rect but here i am not getting any rect as argument, so is it ok to specify my own desired rect and display the pixmap within that rect?

wysota
14th January 2009, 00:26
hmmm maybe. i'll explore about this. So, if i use CSS can i customize everything i have mentioned in my queries?
Well... so far we were basically tallking about a custom checkbox, so yes.


okie, so i should not modify the rect argument in the drawCheck() function.
Correct.


I suppose, if i subclass QAbstractItemDelegate then i can fully customize the checkbox look.
Yes, but then you also have to handle clicking on the checkbox yourself (detecting the hit area, modifying the model and handle drawing).


One query:
I have basically no idea about QPainter yet, so does the QPainter code i shared earlier (in void CheckboxDelegate::drawCheck) look ok (except modifying the rect offcourse)?
Yeah, it looks fine although it doesn't do much - you force a fill of a rect with a specified colour and then render a pixmap on top of it. I don't see the point of neither making the fill nor filling with that colour and not some other, but the code itself is ok.


I mean if i subclass QAbstractItemDelegate and rewrite the paint function. In the paint function, i'll just display a checkbox pixmap. In the paint() function, i'll get the information about the current model index too:

so i can display different images when a row is highlighted.
Basically you have to look inside the QStyleOptionViewItem object and fetch all the necessary info from it (like the palette, areas to paint, current state of the item, etc.).


Now, in this case, can i specify my own rect and draw the checkbox image as i like?
Yes, you get the area for the whole item and its current state and you can draw as many checks or other things as you like.


You said that in QItemDelegate:drawCheck(), i can't modify the rect but here i am not getting any rect as argument, so is it ok to specify my own desired rect and display the pixmap within that rect?

Yes, provided the rect is within the item's rect which you receive inside the option argument. The painter is clipped anyway, so you wouldn't be able to draw outside of the rectangle even if you wanted to.

montylee
14th January 2009, 00:50
wow, thanks for the reply :)
things seem a bit clear now, i hope i am able to implement it properly.

BTW, i downloaded ur new article which was in the Qt quarterly newsletter. You really are a Qt guru :)

Thanks for your help! I might bug you later on again :)

montylee
14th January 2009, 06:25
hey, i tried using style sheets in my application. They seem to be so simple and i am able to set most of the things. But i have a problem.

I am using QWidget::setStyleSheet to set the style for widgets especially my QTableView. I am able to set the background color, highlight color etc...

Now the problem is how to set background color or image for the checkbox? Since the checkbox is displayed through the delegate automatically, i am not able to modify it.

I tried using QWidget::setStyleSheet in the QItemDelegate::drawCheck() method but it gives a compilation error since setStyleSheet method can be used only for QApplication and QWidget classes.

Now, you said that using Style sheets i would be able to custom display the checkbox, but i am not able to find any info about using style sheets in delegate drawing. Please help me.

montylee
20th January 2009, 16:55
I was able to display a checkbox icon in the entire table view cell. I subclassed QStyledItemDelegate class and rewrote the paint function. Here's the code for reference:


void CheckboxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (index.column() == 3) {
QVariant state = index.model()->data(index, Qt::CheckStateRole);
if (state == Qt::Checked) {
// Calculate rectangle to draw the checkbox icon
QRect rect = option.rect.adjusted(20, 5, -25, -10);
if (option.state & QStyle::State_Selected) {
// Fill the highlight rectangle
painter->fillRect(option.rect, option.palette.highlight());
// Draw "ALL" icon in the first row and checkbox icons in other rows
if (index.row() == 0)
painter->drawPixmap(option.rect, QPixmap("all-on.png"));
else
painter->drawPixmap(rect, QPixmap("check-icon-hl.png"));
} else {
if (index.row() == 0)
painter->drawPixmap(option.rect, QPixmap("all-on.png"));
else
painter->drawPixmap(rect, QPixmap("check-icon.png"));
}
} else {
if (option.state & QStyle::State_Selected)
painter->fillRect(option.rect, option.palette.highlight());
if (index.row() == 0)
painter->drawPixmap(option.rect, QPixmap("all-off.png"));
}
} else {
QStyledItemDelegate::paint(painter, option, index);
}
}

georgep
20th February 2009, 15:10
Thanks for great advice on this great thread !!!!

Problem: can anyone tell me how to move the checkbox to the RIGHT ??

I think it has something to do with const QStyleOptionViewItem &option;
but i simply cant do: option.decorationPosition=QStyleOptionViewItem::Ri ght;
I am subclassing QStyledItemDelegate if it matters...
:crying::crying::(

wysota
20th February 2009, 19:34
Check box is not a decoration so you can't do it this way. You have to provide your own delegate that handles painting and clicking the box in the new position. There is a chance you can move the checkbox to the right if you manage to convince the item it should be there in the first place (for example by reimplementing a proper function from the QStyle subclass you use).

georgep
21st February 2009, 04:36
uhm....any idea what class?..

wysota
21st February 2009, 08:56
If you are not using any specific style then it's best to either use style sheets or implement a Proxy style.

georgep
21st February 2009, 09:54
I did a : myQTableView->setStyleSheet(styleSheet) but unfortunately it doesnt appear to affect the checkbox. Or did you mean something else maybe?


cheers

wysota
21st February 2009, 12:58
Maybe you used a wrong stylesheet? :) I'm not sure if you can move the checkbox to the right using a stylesheet, so you'll probably have to implement a proxy style.

montylee
24th February 2009, 23:29
i think george used the method i gave him i.e. to draw a custom checkbox image in the table view cell and trap mouserelease event inside the cell. So, if the mouse release event falls inside the cell containing the checkbox, then toggle the checkbox.

georgep
1st March 2009, 18:31
monty: yes i did, forever gratefull :)

wysota: i want to implement some filtering on data coming from a model (from a database) and from what I understand the QSortFilterProxyModel is suppose to have been created exactly for this.
What would be the best way to add a column to the proxymodel so that the sourcemodel stays untouched (unaware of the extra column) ? Subclass it ?

wysota
2nd March 2009, 08:36
Either use a model proxy or subclass the original model, depending what's easier in your case.