PDA

View Full Version : QProxyFilterSort sort listView column headers by alaph and by ass. dec.



jfinn88
30th September 2016, 21:38
here is my model definition I would like to implement a QSortFilterProxyModel


//------Begin UserEventLog Class/Model-------//
UserEventLog::UserEventLog(QObject *parent):QAbstractListModel(parent){

}

UserEventLog::~UserEventLog(){

}

int UserEventLog::rowCount(const QModelIndex &parent) const{
Q_UNUSED(parent);
return m_userEventList.count();
}

QHash<int, QByteArray> UserEventLog::roleNames() const{
QHash<int, QByteArray> roleNames;
roleNames.insert(idRole, "id");
roleNames.insert(nameRole, "userName");
roleNames.insert(msgRole, "eventMessage");
roleNames.insert(dateRole, "dateTime");
return roleNames;
}

QVariant UserEventLog::data(const QModelIndex &index, int role) const{
if (index.row() < 0 || index.row() >= m_userEventList.count()){
return QVariant();
}

QVariant text;

if(role == idRole){
userEventLogMsg msg = m_userEventList.at(index.row());
text = msg.id;
}
else if(role == nameRole){
userEventLogMsg msg = m_userEventList.at(index.row());
text = msg.username;
}
else if(role == msgRole){
userEventLogMsg msg = m_userEventList.at(index.row());
text = msg.eventmessage;
}
if(role == dateRole){
userEventLogMsg msg = m_userEventList.at(index.row());
text = msg.datetime;
}
return text;
}

void UserEventLog::addEvent(const userEventLogMsg &msg){
beginInsertRows(QModelIndex(), 0, 0);
m_userEventList.insert(0, msg);
endInsertRows();
}

void UserEventLog::init(){
//emit showBusy(true);
dbConnect();
archiveEvent();
selectEvent();
//emit showBusy(false);
}

bool UserEventLog::dbConnect(){
//---check if database is connected---//
if(!m_selectDataBase.isValid()){
qDebug() << "error in connecting to DB";
m_selectDataBase = QSqlDatabase::addDatabase("QSQLITE", "conn2");
m_selectDataBase.setDatabaseName(Paths::root() + "/userLog.db");

qDebug() << "database connect path: "+Paths::root()+"/userLog.db";
m_selectDataBase.open();
}
else{
qDebug() <<"connected to DB" ;
m_selectDataBase.open();
}
return m_selectDataBase.isValid();
}
bool UserEventLog::selectEvent(){
dbConnect();
QDate beginDate = QDate::currentDate();
QDate endDate = QDate::currentDate();

emit showBusy(true);

QSqlQuery selectQuery("SELECT * FROM userlogevents WHERE dateTime BETWEEN ? and ?", m_selectDataBase);
selectQuery.addBindValue(beginDate);
selectQuery.addBindValue(endDate);
if(selectQuery.exec()){
qDebug()<<"sql statement executed fine";
}
else{
emit xmui->alertMsg(QMessageBox::Warning, "Database Error Message 1", "Error: sql select script..."+selectQuery.lastError().text());
return selectQuery.exec();
}

userEventLogMsg msg;
beginResetModel();
m_userEventList.clear();
while (selectQuery.next()){
msg.id = selectQuery.value(0).toString();
msg.username = selectQuery.value(1).toString();
msg.eventmessage = selectQuery.value(2).toString();
msg.datetime = selectQuery.value(3).toString();
addEvent(msg);
}
endResetModel();
emit showBusy(false);
m_selectDataBase.close();
return selectQuery.exec();
}

bool UserEventLog::archiveEvent(){
//---connect to DB---//
dbConnect();

emit showBusy(true);

QSqlQuery attachDbQry(m_selectDataBase);
QString sql_str = "ATTACH DATABASE '" + Paths::root() + "/userLogArchive.db' as db2";
attachDbQry.prepare(sql_str);

qDebug() << "attached database path: "+sql_str;

//---execute attach DB---//
if(attachDbQry.exec()){
qDebug()<<"attached database succesfully";
}
else{
emit xmui->alertMsg(QMessageBox::Warning, "Database Error Message 2", "Error searching database..."+attachDbQry.lastError().text());
}

//---start DB transaction---//
m_selectDataBase.transaction();

//---get archive date vlaue--//
QSqlQuery dateQry("SELECT * FROM userlogevents", m_selectDataBase);

//---get currentDate minus 30days---//
QString dateStr;
QDate archiveDate = QDate::currentDate().addDays(-30);

qDebug() << "archiveDate: "+archiveDate.toString("yyyy-MM-dd");

//---copy from one Db table to another---//
QSqlQuery copyDataQry(m_selectDataBase);
bool prepareSqlBool;
prepareSqlBool = copyDataQry.prepare("INSERT INTO db2.userlogarchive (userName, eventMessage, dateTime) SELECT userName, eventMessage, dateTime FROM main.userlogevents WHERE dateTime < ?");
copyDataQry.addBindValue(archiveDate);

//---prepare sql copy---//
if(prepareSqlBool){
qDebug()<<"prepare copy sql statement exicuted fine";
}
else{
emit xmui->alertMsg(QMessageBox::Warning, "Database Error Message 3", "Error: prepare script..."+copyDataQry.lastError().text());
}

bool copySqlBool;
copySqlBool = copyDataQry.exec();

QSqlQuery archiveDataQry(m_selectDataBase);
QDate rowDate;

//---execute sql copy---//
if(copySqlBool){
qDebug()<<"insert copy sql statement exicuted fine";

//---Copy rows to archive then delete old rows---//
while(dateQry.next()){
//---Get date value from dateQry---//
dateStr = dateQry.value(3).toString();
rowDate = QDate::fromString(dateStr, "yyyy-MM-dd");

//---Executes only if copy qry executes---//
if(rowDate < archiveDate){

//---Sql delete statement---//
archiveDataQry.prepare("DELETE FROM userlogevents WHERE dateTime < ?");
archiveDataQry.addBindValue(archiveDate);

//---execute delete---//
if(archiveDataQry.exec()){
qDebug()<<"delete sql statement exicuted fine";
}
else{
emit xmui->alertMsg(QMessageBox::Warning, "Database Error Message 5", "Error: deleting old data..."+archiveDataQry.lastError().text());
//m_selectDataBase.rollback();
return archiveDataQry.exec();
}
}
else{
//qDebug() << "no old records to delete";
return false;
}
}
}
else{
emit xmui->alertMsg(QMessageBox::Warning, "Database Error Message 4", "Error: copy/insert archive data script..."+copyDataQry.lastError().text());
//m_selectDataBase.rollback();
return copySqlBool;
}

//---commit transaction & close---//
m_selectDataBase.commit();
m_selectDataBase.close();
emit showBusy(false);
return copySqlBool;
}

//---Leaving out search fcn definitions---//


I'm just not sure how to start implementing the QSortFilterProxyModel does the sortfilter model take the place of my current model ? do I create a separate class for the proxymodel? I want to be able to sort my list that is created form my model.



//---Column Headers for listView table---//
Rectangle {
id: eventMsg_panel
height: 30
radius: 8
color: "#141414"
anchors.right: parent.right
anchors.rightMargin: 6
anchors.left: parent.left
anchors.leftMargin: 5
anchors.top: parent.top
anchors.topMargin: 50
border.width: 5
border.color: "#00000000"
Rectangle{
id: labels
color: "#333131"
radius: 8
anchors.top: parent.top
anchors.topMargin: 0
border.color: "#00000000"
border.width: 2
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
Rectangle{
id: id_col
width: 100
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
color: "#00000000"
Text {
color: "#ffffff"
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
verticalAlignment: Text.AlignVCenter
text: "ID";
font.pixelSize: 20
horizontalAlignment: Text.AlignHCenter
font.bold: false
}
}
Rectangle{
id:divider1
width:3
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: id_col.right
color: "#8a8a8a"
}
Rectangle{
id: userName_col
width: 150
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: divider1.right
color: "#00000000"
Text {
color: "#ffffff"
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
verticalAlignment: Text.AlignVCenter
text: "User Name";
font.pixelSize: 20
horizontalAlignment: Text.AlignHCenter
font.bold: false
}
}
Rectangle{
id:divider2
width:3
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: userName_col.right
color: "#8a8a8a"
}
Rectangle{
id: eventMsg_col
height: 25
width: 200
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: divider2.right
anchors.right: parent.right
color: "#00000000"
anchors.rightMargin: 156
Text {
height: 20
color: "#ffffff"
verticalAlignment: Text.AlignVCenter
text: "Event Message";
anchors.horizontalCenterOffset: -50
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 20
font.bold: false
}
}
Rectangle{
id:divider3
width:3
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: eventMsg_col.right
color: "#8a8a8a"
}
Rectangle{
id: dateTime_col
width: 150
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: divider3.right
color: "#00000000"
Text {
color: "#ffffff"
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
verticalAlignment: Text.AlignVCenter
text: "Date";
font.pixelSize: 20
horizontalAlignment: Text.AlignHCenter
font.bold: false
}
}
}
}





ScrollView{
id: userEvent_scrollView
anchors.fill: parent
anchors.bottomMargin: 5
anchors.topMargin: 5
anchors.rightMargin: 5
anchors.leftMargin: 5
//---Enables flickable Area---//
flickableItem.interactive: true
//---ScrollBar Component---//
style: edit_scrollbar_style
ListView {
id: listView
anchors.fill: parent
model: UserEventLog
delegate: msgDelegate
keyNavigationWraps: true
KeyNavigation.tab: userNameDropDown
orientation : "Vertical"
snapMode: ListView.SnapToItem
boundsBehavior: Flickable.StopAtBounds
clip: true;
highlightMoveVelocity: 2000
highlight: Rectangle{
radius: 7;
color: "red"
}
}
}
}

anda_skoa
1st October 2016, 10:58
You just put the proxy model in between the actual model and the view.

I.e. set the actual model on the proxy via QSortFilterProxyModel::setSourceModel() and then use the proxy as the view's model.

Cheers,
_

jfinn88
3rd October 2016, 18:56
I just want to use the virtual sort() function of QSortFilterProxyModel class to sort my columns in my list view if the header/column title is clicked()...

I add a UserEventModelProxy class that sub classes QSortFilterPRoxyModel class (I only create a construct cuase I just want to use a the virtual function sort() and this function should get called when the constructor dose...)

I created an instance of my UserEventModelProxy class to use in my UserEventLog class
I set the sourceModel and set the sortRole to UserEventLog::dateRole
then set the sort to the date column (it seems this sort is ran when the model is going to populate and not on a signal I want to sort on signal I don’t want sort to happen before)

I need to connect the column title/head to the sort() virtual function call...



#include "usereventlog.h"


//---UserEvent-SortModelProxy---//
UserEventModelProxy::UserEventModelProxy(QObject *parent) : QSortFilterProxyModel(parent){

}
//----------End QSortFilterProxy----------//



//------Begin UserEventLog Class/Model-------//
UserEventLog::UserEventLog(QObject *parent):QAbstractListModel(parent){

//---allocates dynamic memory on heap | new instance of proxyModel class---//
m_UserEventModelProxy = new UserEventModelProxy(this);

//---Sets the model the proxySort will use e.g. UserEventLog Model---//
m_UserEventModelProxy->setSourceModel(this);

//---property holds the item role that is used to query the source model's data when sorting items---//
m_UserEventModelProxy->setSortRole(UserEventLog::dateRole);

//---Sorts the model by column in the given order---//
//---Date Column---//
m_UserEventModelProxy->sort(4);
}

UserEventLog::~UserEventLog(){

}

int UserEventLog::rowCount(const QModelIndex &parent) const{
Q_UNUSED(parent);
return m_userEventList.count();
}

QHash<int, QByteArray> UserEventLog::roleNames() const{
QHash<int, QByteArray> roleNames;
roleNames.insert(idRole, "id");
roleNames.insert(nameRole, "userName");
roleNames.insert(msgRole, "eventMessage");
roleNames.insert(dateRole, "dateTime");
return roleNames;
}

QVariant UserEventLog::data(const QModelIndex &index, int role) const{
if (index.row() < 0 || index.row() >= m_userEventList.count()){
return QVariant();
}

QVariant text;

if(role == idRole){
userEventLogMsg msg = m_userEventList.at(index.row());
text = msg.id;
}
else if(role == nameRole){
userEventLogMsg msg = m_userEventList.at(index.row());
text = msg.username;
}
else if(role == msgRole){
userEventLogMsg msg = m_userEventList.at(index.row());
text = msg.eventmessage;
}
if(role == dateRole){
userEventLogMsg msg = m_userEventList.at(index.row());
text = msg.datetime;
}
return text;
}

void UserEventLog::addEvent(const userEventLogMsg &msg){
beginInsertRows(QModelIndex(), 0, 0);
m_userEventList.insert(0, msg);
endInsertRows();
}

void UserEventLog::init(){
//emit showBusy(true);
dbConnect();
archiveEvent();
selectEvent();
//emit showBusy(false);
}

bool UserEventLog::dbConnect(){
//---check if database is connected---//
if(!m_selectDataBase.isValid()){
qDebug() << "error in connecting to DB";
m_selectDataBase = QSqlDatabase::addDatabase("QSQLITE", "conn2");
m_selectDataBase.setDatabaseName(Paths::root() + "/userLog.db");

qDebug() << "database connect path: "+Paths::root()+"/userLog.db";
m_selectDataBase.open();
}
else{
qDebug() <<"connected to DB" ;
m_selectDataBase.open();
}
return m_selectDataBase.isValid();
}
bool UserEventLog::selectEvent(){
dbConnect();
QDate beginDate = QDate::currentDate();
QDate endDate = QDate::currentDate();

emit showBusy(true);

QSqlQuery selectQuery("SELECT * FROM userlogevents WHERE dateTime BETWEEN ? and ?", m_selectDataBase);
selectQuery.addBindValue(beginDate);
selectQuery.addBindValue(endDate);
if(selectQuery.exec()){
qDebug()<<"sql statement executed fine";
}
else{
emit xmui->alertMsg(QMessageBox::Warning, "Database Error Message 1", "Error: sql select script..."+selectQuery.lastError().text());
return selectQuery.exec();
}

userEventLogMsg msg;
beginResetModel();
m_userEventList.clear();
while (selectQuery.next()){
msg.id = selectQuery.value(0).toString();
msg.username = selectQuery.value(1).toString();
msg.eventmessage = selectQuery.value(2).toString();
msg.datetime = selectQuery.value(3).toString();
addEvent(msg);
}
endResetModel();
emit showBusy(false);
m_selectDataBase.close();
return selectQuery.exec();
}





class UserEventModelProxy : public QSortFilterProxyModel
{
Q_OBJECT

public:
UserEventModelProxy(QObject *parent = 0);
};

//---Data struct for user event log---//
struct userEventLogMsg{
//---hold all values for a single list entry---//
QString id;
QString username;
QString eventmessage;
QString datetime;
};

//---Class UserEventDailyLog | Subed classed: QAbstractTableModel---//
class UserEventLog : public QAbstractListModel
{
Q_OBJECT

public:
explicit UserEventLog(QObject *parent = 0);

~UserEventLog();

enum userEventRoles {idRole= Qt::UserRole + 220, nameRole, msgRole, dateRole};

int rowCount(const QModelIndex & parent) const;

QHash<int, QByteArray> roleNames() const;

QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;

Q_INVOKABLE void addEvent(const userEventLogMsg &msg);

Q_INVOKABLE bool archiveEvent();

Q_INVOKABLE bool selectEvent();

Q_INVOKABLE bool dbConnect();

Q_INVOKABLE void init();

Q_INVOKABLE bool searchDate(QDate userDateText);

Q_INVOKABLE bool searchUserName(QString userNameText);

Q_INVOKABLE bool searchUserNameDateText(QString userNameText, QDate userDateText);

Q_INVOKABLE bool searchDateRange(QDate beginDate, QDate endDate);

Q_INVOKABLE bool searchDateRange(const QString &userName, QDate beginDate, QDate endDate);

signals:
void showBusy(bool busy_state);

void alertDbMsg(int icon, QString title, QString msg_text);

private:
QList<userEventLogMsg> m_userEventList;
QSqlDatabase m_selectDataBase;
QSqlQuery m_selectQuery;

//---Instance of proxy model class---//
UserEventModelProxy *m_UserEventModelProxy;
};
//--------------------End of userLogEvent Class-----------------//




//-------------Create instance of class | Set the context property to expose C++ to QML------------//
mUserEventLogModel = new UserEventLog();
m_QmlEngine->rootContext()->setContextProperty("UserEventLog", mUserEventLogModel);

mUserEventProxyModel = new UserEventModelProxy();
m_QmlEngine->rootContext()->setContextProperty("UserEventProxyModel", mUserEventProxyModel);
//--------------------------------------------------------------//


Added after 4 minutes:

qml


//---Column Headers for listView table---//
Rectangle {
id: eventMsg_panel
height: 30
radius: 8
color: "#141414"
anchors.right: parent.right
anchors.rightMargin: 6
anchors.left: parent.left
anchors.leftMargin: 5
anchors.top: parent.top
anchors.topMargin: 50
border.width: 5
border.color: "#00000000"
Rectangle{
id: labels
color: "#333131"
radius: 8
anchors.top: parent.top
anchors.topMargin: 0
border.color: "#00000000"
border.width: 2
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
Rectangle{
id: id_col
width: 100
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
color: "#00000000"
Text {
color: "#ffffff"
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
verticalAlignment: Text.AlignVCenter
text: "ID";
font.pixelSize: 20
horizontalAlignment: Text.AlignHCenter
font.bold: false
}
}
Rectangle{
id:divider1
width:3
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: id_col.right
color: "#8a8a8a"
}
Rectangle{
id: userName_col
width: 150
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: divider1.right
color: "#00000000"
Text {
color: "#ffffff"
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
verticalAlignment: Text.AlignVCenter
text: "User Name";
font.pixelSize: 20
horizontalAlignment: Text.AlignHCenter
font.bold: false
}
}
Rectangle{
id:divider2
width:3
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: userName_col.right
color: "#8a8a8a"
}
Rectangle{
id: eventMsg_col
height: 25
width: 200
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: divider2.right
anchors.right: parent.right
color: "#00000000"
anchors.rightMargin: 156
Text {
height: 20
color: "#ffffff"
verticalAlignment: Text.AlignVCenter
text: "Event Message";
anchors.horizontalCenterOffset: -50
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 20
font.bold: false
}
}
Rectangle{
id:divider3
width:3
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: eventMsg_col.right
color: "#8a8a8a"
}
Rectangle{
id: dateTime_col
width: 150
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: divider3.right
color: "#00000000"
Text {
color: "#ffffff"
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
verticalAlignment: Text.AlignVCenter
text: "Date";
font.pixelSize: 20
horizontalAlignment: Text.AlignHCenter
font.bold: false
}
}
}
}


qml continued



//---Delegate Component for listView | controls key nav & highlighting---//
Component {
id: msgDelegate
Item {
id: active_eventMsg
anchors.left: parent.left
anchors.leftMargin: 5
anchors.right: parent.right
anchors.rightMargin:5
height: 30

//---Key Navigation Work Around (listView focus issue)---//
Keys.onDownPressed: listView.incrementCurrentIndex()
Keys.onUpPressed: listView.decrementCurrentIndex()
KeyNavigation.tab: userNameDropDown
activeFocusOnTab: true

//---Highlight Functionality---//
MouseArea {
anchors.fill: parent
onClicked: {
listView.focus = true
console.log("msgDelegate activeFocus: "+activeFocus)
listView.currentIndex = index
}
}
//---Rect display model data in listView---//
Rectangle{
id: id_rect
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: 2
width: id_col.width
height: parent.height
color: "#00000000"
Text {
color: active_eventMsg.ListView.isCurrentItem ? "white" : "black"
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
text: id;
font.pixelSize: 18
}
}
Rectangle{
id: userName_rect
anchors.top: parent.top
anchors.left: id_rect.right
anchors.leftMargin: 2
width: userName_col.width
height: parent.height
color: "#00000000"
Text {
color: active_eventMsg.ListView.isCurrentItem ? "white" : "black"
anchors.fill: parent
anchors.leftMargin: 2
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
text: userName;
font.pixelSize: 18
}
}
Rectangle{
id: eventMsg_rect
anchors.top: parent.top
anchors.left: userName_rect.right
anchors.leftMargin: 2
width: eventMsg_col.width
height: parent.height
color: "#00000000"
Text {
color: active_eventMsg.ListView.isCurrentItem ? "white" : "black"
anchors.fill: parent
anchors.leftMargin: 2
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
text: eventMessage;
font.pixelSize: 18
}
}
Rectangle{
id: dateTime_rect
anchors.top: parent.top
anchors.left: eventMsg_rect.right
anchors.leftMargin: 2
width: dateTime_col.width
anchors.right: parent.right
height: parent.height
color: "#00000000"
Text {
color: active_eventMsg.ListView.isCurrentItem ? "white" : "black"
anchors.fill: parent
anchors.leftMargin: 2
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
text: dateTime;
font.pixelSize: 18
}
}
}
}

//---Rect for listView---//
Rectangle {
id: listView_rect
radius: 8
anchors.top: eventMsg_panel.bottom
anchors.topMargin: 2
anchors.bottom: parent.bottom
anchors.bottomMargin: 65
anchors.left:parent.left
anchors.leftMargin: 5
anchors.right:parent.right
anchors.rightMargin: 6
border{
color: "black"
width: 3
}
//---Adds ScrollBars---//
ScrollView{
id: userEvent_scrollView
anchors.fill: parent
anchors.bottomMargin: 5
anchors.topMargin: 5
anchors.rightMargin: 5
anchors.leftMargin: 5
//---Enables flickable Area---//
flickableItem.interactive: true
//---ScrollBar Component---//
style: edit_scrollbar_style
ListView {
id: listView
anchors.fill: parent
model: UserEventLog
delegate: msgDelegate
keyNavigationWraps: true
KeyNavigation.tab: userNameDropDown
orientation : "Vertical"
snapMode: ListView.SnapToItem
boundsBehavior: Flickable.StopAtBounds
clip: true;
highlightMoveVelocity: 2000
highlight: Rectangle{
radius: 7;
color: "red"
}
}
}
}


Added after 5 minutes:

"To make your data sortable, you can either implement sort() in your model, " -doc

I want to reimplement sort but not sure how and also don't want it sort anything when it first displays the model

Added after 39 minutes:

update: override the sort() method

added sortByColumn() method that call the sort function

need to connect it now...



void UserEventModelProxy::sortByColumn(int column, Qt::SortOrder order)
{
sort(column, order);
}


Added after 23 minutes:

can yo set the sort indicator for a listView?

jfinn88
3rd October 2016, 21:53
I set my model of my listView to my proxy model but nothing displays in the listView using the proxyModel

Added after 1 46 minutes:

One thing i realized is I only have one column with the way my model works ...

jfinn88
4th October 2016, 00:25
update:
using lessThan custom function to determine which data get placed below when sorting

using proxyFilter instance in my UserEventLog class constructor

setRootContext to expose proxySortFilter to qml

Set the listView's model to the proxySortFilter model using the rootContext set

created a mouse area in qml with an onClicked call -> on this click I want to sort based of what column they click(since its a list view each row of data only technically has one column I just talking about when they click on the visual display for the column heading in qml)

Issue 1: I don’t want data sorted when just displaying model I only want to sort if the column heading is clicked (not sure if listView best way but I really like how It visually looks so far and want to keep the listView style)

Issue 2: calling sort from qml onClicked signal


custom lessThan()


//---The QModelIndex class is used to locate data in a data model | Overrides lessThan()---//
bool UserEventModelProxy::lessThan(const QModelIndex &one, const QModelIndex &two) const {
qDebug() << "made it inside lessThan";

//---Create boolean values from the modelIndex passed as parameters---//
bool entry1 = sourceModel()->data(one, UserEventLog::dateRole).toBool();
bool entry2 = sourceModel()->data(two, UserEventLog::dateRole).toBool();

//---Check the boolean values for data---//
if(entry1 && entry2){
return QSortFilterProxyModel::lessThan(one, two);
}
if(entry1 && !entry2){
return false;
}
if(!entry1 && entry2){
return true;
}

//---Sort the ListView model based off the role type passed/selected---//
if(sortRole() == UserEventLog::idRole){
QVariant dataOne = sourceModel()->data(one, UserEventLog::idRole);
QVariant dataTwo = sourceModel()->data(two, UserEventLog::idRole);

QDateTime dateOne = QDateTime::fromString(dataOne.toString());
QDateTime dateTwo = QDateTime::fromString(dataTwo.toString());
if (dateOne != dateTwo) {
return dateOne < dateTwo;
}
else {
QVariant nameOne = sourceModel()->data(one, UserEventLog::idRole);
QVariant nameTwo = sourceModel()->data(two, UserEventLog::idRole);
return (nameOne.toString() < nameTwo.toString());
}
}
else if(sortRole() == UserEventLog::nameRole){
QVariant dataOne = sourceModel()->data(one, UserEventLog::nameRole);
QVariant dataTwo = sourceModel()->data(two, UserEventLog::nameRole);

QDateTime dateOne = QDateTime::fromString(dataOne.toString());
QDateTime dateTwo = QDateTime::fromString(dataTwo.toString());
if (dateOne != dateTwo) {
return dateOne < dateTwo;
}
else {
QVariant nameOne = sourceModel()->data(one, UserEventLog::nameRole);
QVariant nameTwo = sourceModel()->data(two, UserEventLog::nameRole);
return (nameOne.toString() < nameTwo.toString());
}
}
else if(sortRole() == UserEventLog::msgRole){
QVariant dataOne = sourceModel()->data(one, UserEventLog::msgRole);
QVariant dataTwo = sourceModel()->data(two, UserEventLog::msgRole);

QDateTime dateOne = QDateTime::fromString(dataOne.toString());
QDateTime dateTwo = QDateTime::fromString(dataTwo.toString());
if (dateOne != dateTwo) {
return dateOne < dateTwo;
}
else {
QVariant nameOne = sourceModel()->data(one, UserEventLog::msgRole);
QVariant nameTwo = sourceModel()->data(two, UserEventLog::msgRole);
return (nameOne.toString() < nameTwo.toString());
}
}
else if(sortRole() == UserEventLog::dateRole){
QVariant dataOne = sourceModel()->data(one, UserEventLog::dateRole);
QVariant dataTwo = sourceModel()->data(two, UserEventLog::dateRole);

QDateTime dateOne = QDateTime::fromString(dataOne.toString(),"M/d/yyyy hh:mm AP");
QDateTime dateTwo = QDateTime::fromString(dataTwo.toString(), "M/d/yyyy hh:mm AP");
if (dateOne != dateTwo) {
return dateOne < dateTwo;
}
else {
QVariant nameOne = sourceModel()->data(one, UserEventLog::dateRole);
QVariant nameTwo = sourceModel()->data(two, UserEventLog::dateRole);
return (nameOne.toString() < nameTwo.toString());
}
}
else{
return QSortFilterProxyModel::lessThan(one, two);
}
}


userEventLog constructor (may need to change this since I want sort fcn call on qml onclicked event



//------Begin UserEventLog Class/Model-------//
UserEventLog::UserEventLog(QObject *parent):QAbstractListModel(parent){

//---allocates dynamic memory on heap | Instance of proxyModel class---//
m_UserEventModelProxy = new UserEventModelProxy(this);

//---property holds the source model for proxy model---//
m_UserEventModelProxy->setSourceModel(this);

//---property holds the case sensitivity setting used for comparing strings when sorting---//
m_UserEventModelProxy->setSortCaseSensitivity(Qt::CaseInsensitive);

//---property holds the item role that is used to query the source model's data when sorting items---//
m_UserEventModelProxy->setSortRole(UserEventLog::nameRole);

//m_UserEventModelProxy->setSortRole(UserEventLog::nameRole);
//m_UserEventModelProxy->setSortRole(UserEventLog::msgRole);
//m_UserEventModelProxy->setSortRole(UserEventLog::dateRole);

//---Sorts the model by column in the given order | this calls lessThan() ---//
m_UserEventModelProxy->sort(0);
}



qml (visual headers for listView)


//---Column Headers for listView table---//
Rectangle {
id: eventMsg_panel
height: 30
radius: 8
color: "#141414"
anchors.right: parent.right
anchors.rightMargin: 6
anchors.left: parent.left
anchors.leftMargin: 5
anchors.top: parent.top
anchors.topMargin: 50
border.width: 5
border.color: "#00000000"
Rectangle{
id: labels
color: "#333131"
radius: 8
anchors.top: parent.top
anchors.topMargin: 0
border.color: "#00000000"
border.width: 2
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
Rectangle{
id: id_col
width: 100
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
color: "#00000000"
Text {
color: "#ffffff"
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
verticalAlignment: Text.AlignVCenter
text: "ID";
font.pixelSize: 20
horizontalAlignment: Text.AlignHCenter
font.bold: false
}
MouseArea{
id: id_col_mouseArea
anchors.fill: parent
onClicked: {
UserEventModelProxy.sortRole(idRole);
}
}
}
Rectangle{
id:divider1
width:3
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: id_col.right
color: "#8a8a8a"
}
Rectangle{
id: userName_col
width: 150
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: divider1.right
color: "#00000000"
Text {
color: "#ffffff"
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
verticalAlignment: Text.AlignVCenter
text: "User Name";
font.pixelSize: 20
horizontalAlignment: Text.AlignHCenter
font.bold: false
}
MouseArea{
id: userName_col_mouseArea
anchors.fill: parent
onClicked: {
UserEventModelProxy.sortRole(nameRole);
}
}
}
Rectangle{
id:divider2
width:3
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: userName_col.right
color: "#8a8a8a"
}


qml listView model


//---Delegate Component for listView | controls key nav & highlighting---//
Component {
id: msgDelegate
Item {
id: active_eventMsg
anchors.left: parent.left
anchors.leftMargin: 5
anchors.right: parent.right
anchors.rightMargin:5
height: 30

//---Key Navigation Work Around (listView focus issue)---//
Keys.onDownPressed: listView.incrementCurrentIndex()
Keys.onUpPressed: listView.decrementCurrentIndex()
KeyNavigation.tab: userNameDropDown
activeFocusOnTab: true

//---Highlight Functionality---//
MouseArea {
anchors.fill: parent
onClicked: {
listView.focus = true
console.log("msgDelegate activeFocus: "+activeFocus)
listView.currentIndex = index
}
}
//---Rect display model data in listView---//
Rectangle{
id: id_rect
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: 2
width: id_col.width
height: parent.height
color: "#00000000"
Text {
color: active_eventMsg.ListView.isCurrentItem ? "white" : "black"
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
text: id;
font.pixelSize: 18
}
}
Rectangle{
id: userName_rect
anchors.top: parent.top
anchors.left: id_rect.right
anchors.leftMargin: 2
width: userName_col.width
height: parent.height
color: "#00000000"
Text {
color: active_eventMsg.ListView.isCurrentItem ? "white" : "black"
anchors.fill: parent
anchors.leftMargin: 2
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
text: userName;
font.pixelSize: 18
}
}
Rectangle{
id: eventMsg_rect
anchors.top: parent.top
anchors.left: userName_rect.right
anchors.leftMargin: 2
width: eventMsg_col.width
height: parent.height
color: "#00000000"
Text {
color: active_eventMsg.ListView.isCurrentItem ? "white" : "black"
anchors.fill: parent
anchors.leftMargin: 2
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
text: eventMessage;
font.pixelSize: 18
}
}
Rectangle{
id: dateTime_rect
anchors.top: parent.top
anchors.left: eventMsg_rect.right
anchors.leftMargin: 2
width: dateTime_col.width
anchors.right: parent.right
height: parent.height
color: "#00000000"
Text {
color: active_eventMsg.ListView.isCurrentItem ? "white" : "black"
anchors.fill: parent
anchors.leftMargin: 2
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
text: dateTime;
font.pixelSize: 18
}
}
}
}

//---Rect for listView---//
Rectangle {
id: listView_rect
radius: 8
anchors.top: eventMsg_panel.bottom
anchors.topMargin: 2
anchors.bottom: parent.bottom
anchors.bottomMargin: 65
anchors.left:parent.left
anchors.leftMargin: 5
anchors.right:parent.right
anchors.rightMargin: 6
border{
color: "black"
width: 3
}
//---Adds ScrollBars---//
ScrollView{
id: userEvent_scrollView
anchors.fill: parent
anchors.bottomMargin: 5
anchors.topMargin: 5
anchors.rightMargin: 5
anchors.leftMargin: 5
//---Enables flickable Area---//
flickableItem.interactive: true
//---ScrollBar Component---//
style: edit_scrollbar_style
ListView {
id: listView
anchors.fill: parent
model: UserEventModelProxy
delegate: msgDelegate
keyNavigationWraps: true
KeyNavigation.tab: userNameDropDown
orientation : "Vertical"
snapMode: ListView.SnapToItem
boundsBehavior: Flickable.StopAtBounds
clip: true;
highlightMoveVelocity: 2000
highlight: Rectangle{
radius: 7;
color: "red"
}
}
}
}


I'm not sure what or how to call the proxyModel lessThan() from qml onClicked

I'm not sure if calling sort() form UserEventLog constructor is right way to do it since I don’t want the data sorted at first....

jfinn88
4th October 2016, 20:45
update: made some good improvements but still having issues implementing proxysortfilter

I created a proxyModel class

I implemented my own custom lessThan() function in my proxyModel class

I created a rootContext Item to expose proxy model to c++ set the listView model to the proxy model

In the constructor of my UserEvetnLog class I create a instance of the proxyModel class in my userEventLog class
I set the sourceModel of the proxySortFilter to the UserEventLog model
I set the dynamicSortFilter
I set the caseSensitivity to insensitive

I created a function for setting the sortRole in my UserEventLog class

I use Q_INVOKABLE and the UserEventLog rootContext item to call the setSortRole() fucntion in QML
inside the setSortRole() function I check what roleName is being set to sort the data accordingly I call sort() which calls the virtual function lessThan()

with debug I can see I make it inside my setSortRole() function then form there I can see it enter the lessThan() with debugs however nothing displays in the model listView

nothing loads to the listView model at first either when dialog is loaded

When the model first loads I don’t want it sorted only sort onClicked function call in qml...



proxyModel lessTahn()
//---The QModelIndex class is used to locate data in a data model | Overrides lessThan()---//
bool UserEventModelProxy::lessThan(const QModelIndex &one, const QModelIndex &two) const {

//---Sort the ListView model based off the roleType---//
if(sortRole() == UserEventLog::idRole){
QVariant idOne = sourceModel()->data(one, UserEventLog::idRole);
QVariant idTwo = sourceModel()->data(two, UserEventLog::idRole);

qDebug() << "made it inside else-if UserEvetnLog::idRole condition";

QString int_IdOne = idOne.toString();
QString int_IdTwo = idTwo.toString();

if (int_IdOne != int_IdTwo) {
qDebug() << "int_IdOne != int_IdTwo";
return int_IdOne < int_IdTwo;
}
//---Second sort criteria---//
else {
qDebug() << "else int_IdOne != int_IdTwo";
QVariant idOne = sourceModel()->data(one, UserEventLog::nameRole);
QVariant idTwo = sourceModel()->data(two, UserEventLog::nameRole);
return (idOne.toInt() < idTwo.toInt());
}
}
else if(sortRole() == UserEventLog::nameRole){
QVariant nameOne = sourceModel()->data(one, UserEventLog::nameRole);
QVariant nameTwo = sourceModel()->data(two, UserEventLog::nameRole);

qDebug() << "made it inside else-if UserEvetnLog::nameRole condition";

QString str_NameOne = nameOne.toString();
QString str_NameTwo = nameTwo.toString();

if (str_NameOne != str_NameTwo) {
qDebug() << "str_NameOne != str_NameTwo";
return str_NameOne < str_NameTwo;
}
else {
qDebug() << "else str_NameOne != str_NameTwo";
QVariant nameOne = sourceModel()->data(one, UserEventLog::dateRole);
QVariant nameTwo = sourceModel()->data(two, UserEventLog::dateRole);
return (nameOne.toString() < nameTwo.toString());
}
}
else if(sortRole() == UserEventLog::msgRole){
QVariant msgOne = sourceModel()->data(one, UserEventLog::msgRole);
QVariant msgTwo = sourceModel()->data(two, UserEventLog::msgRole);

qDebug() << "made it inside else-if UserEvetnLog::msgRole condition";

QString str_msgOne = msgOne.toString();
QString str_msgTwo = msgTwo.toString();

if (str_msgOne != str_msgTwo) {
return str_msgOne < str_msgTwo;
}
else {
QVariant msgOne = sourceModel()->data(one, UserEventLog::nameRole);
QVariant msgTwo = sourceModel()->data(two, UserEventLog::nameRole);
return (msgOne.toString() < msgTwo.toString());
}
}
else if(sortRole() == UserEventLog::dateRole){
QVariant dataOne = sourceModel()->data(one, UserEventLog::dateRole);
QVariant dataTwo = sourceModel()->data(two, UserEventLog::dateRole);

qDebug() << "made it inside else-if UserEvetnLog::dateRole condition";

QString dateOne = dataOne.toString();
QString dateTwo = dataTwo.toString();
if (dateOne != dateTwo) {
return dateOne < dateTwo;
}
else {
QVariant dataOne = sourceModel()->data(one, UserEventLog::nameRole);
QVariant dataTwo = sourceModel()->data(two, UserEventLog::nameRole);
return (dataOne.toString() < dataTwo.toString());
}
}
else{
return QSortFilterProxyModel::lessThan(one, two);
}
}

//------Begin UserEventLog Class/Model-------//
UserEventLog::UserEventLog(QObject *parent):QAbstractListModel(parent){

//---allocates dynamic memory on heap | Instance of proxyModel class---//
m_UserEventModelProxy = new UserEventModelProxy(this);
m_UserEventModelProxy->setSourceModel(this);
m_UserEventModelProxy->setDynamicSortFilter(true);
m_UserEventModelProxy->setSortCaseSensitivity(Qt::CaseInsensitive);
//m_UserEventModelProxy->setSortRole(UserEventLog::idRole);
//m_UserEventModelProxy->sort(0);
}

//---ProxyModel Sort setRoleName---//
void UserEventLog::setSortRole (int col)
{
switch(col){
case 0:
//---idRole---//
qDebug() << "case 0";
if(m_UserEventModelProxy->sortRole() == UserEventLog::idRole){
qDebug() << "inside if sortRole() == UserEventLog::idRole";
if(m_UserEventModelProxy->sortOrder() == Qt::AscendingOrder)
m_UserEventModelProxy->sort(0, Qt::DescendingOrder);
else
m_UserEventModelProxy->sort(0, Qt::AscendingOrder);
}else{
qDebug() << "inside else sortRole() == UserEventLog::idRole";
m_UserEventModelProxy->setSortRole(UserEventLog::idRole);
m_UserEventModelProxy->sort(0);
}
break;
case 1:
//---nameRole---//
qDebug() << "case 1";
if(m_UserEventModelProxy->sortRole() == UserEventLog::nameRole){
if(m_UserEventModelProxy->sortOrder() == Qt::AscendingOrder)
m_UserEventModelProxy->sort(0, Qt::DescendingOrder);
else
m_UserEventModelProxy->sort(0, Qt::AscendingOrder);
}else{
m_UserEventModelProxy->setSortRole(UserEventLog::nameRole);
m_UserEventModelProxy->sort(0);
}
break;
case 2:
//---msgRole---//
qDebug() << "case 2";
if(m_UserEventModelProxy->sortRole() == UserEventLog::msgRole){
if(m_UserEventModelProxy->sortOrder() == Qt::AscendingOrder)
m_UserEventModelProxy->sort(0, Qt::DescendingOrder);
else
m_UserEventModelProxy->sort(0, Qt::AscendingOrder);
}else{
m_UserEventModelProxy->setSortRole(UserEventLog::msgRole);
m_UserEventModelProxy->sort(0);
}
break;
case 3:
//---dateRole---//
qDebug() << "case 3";
if(m_UserEventModelProxy->sortRole() == UserEventLog::dateRole){
if(m_UserEventModelProxy->sortOrder() == Qt::AscendingOrder)
m_UserEventModelProxy->sort(0, Qt::DescendingOrder);
else
m_UserEventModelProxy->sort(0, Qt::AscendingOrder);
}else{
m_UserEventModelProxy->setSortRole(UserEventLog::dateRole);
m_UserEventModelProxy->sort(0);
}
break;
}
}

QML mouseArea to call setSortRole()
MouseArea{
id: id_col_mouseArea
anchors.fill: parent
onClicked: {
console.log("id_col_mouseArea clicked")
UserEventLog.setSortRole(0);
}
}

qml model listView
ListView {
id: listView
anchors.fill: parent
//---Proxy Model for sorting---//
model: UserEventModelProxy
delegate: msgDelegate
keyNavigationWraps: true
KeyNavigation.tab: userNameDropDown
orientation : "Vertical"
snapMode: ListView.SnapToItem
boundsBehavior: Flickable.StopAtBounds
clip: true;
highlightMoveVelocity: 2000
highlight: Rectangle{
radius: 7;
color: "red"
}
}

anda_skoa
6th October 2016, 16:02
I have not looked at all the code, but are you sure the model you expose through the root context is the model you need?

Some of your code seems to indicate that you create a useless sort filter proxy model inside the main model and the expose another, empty, proxy model to QML.

Cheers,
_