2017-08-08 27 views
1

私はチャットクライアントを作っていますが、qml ListViewを使用して、すべてのメッセージを「チャットルーム」に表示しています。QSortFilterProxyModelを使用してQml ListViewのデータを並べ替えます。

私は、ユーザーがメンバーであるすべての部屋にすべてのメッセージを格納するためにQAbstractListModelから派生した独自のモデルクラスを使用しています。

最初に送信したタイムスタンプに基づいてすべてのメッセージを並べ替えたい場合は、最新のメッセージがビューの一番下に表示され、最も古いメッセージが上部に表示されます。

私はすでに送信した部屋に基づいてメッセージをフィルタリングできるようにしたいと思います。私はすでに解決しています。ここで

は、ここに私のカスタムモデルの宣言

class MessageModel : public QAbstractListModel 
{ 
public: 
    enum MessageRoles { 
      Id = Qt::UserRole + 1, 
      RoomId = Qt::UserRole + 2, 
      PersonId = Qt::UserRole + 3, 
      PersonEmail = Qt::UserRole + 4, 
      Created = Qt::UserRole + 5, 
      Text = Qt::UserRole + 6 
     }; 
    explicit MessageModel(QObject * parent = nullptr); 
    int rowCount(const QModelIndex &parent = QModelIndex()) const override; 
    QHash<int, QByteArray> roleNames() const; 
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; 
    void reset_data(QJsonArray new_data); 
    void insert_unique_data(QJsonArray new_data); 
private: 
    QList<LocalData::Message> m_data; 

}; 

は私のモデルの実装

MessageModel::MessageModel(QObject *parent) : QAbstractListModel(parent) 
{} 

int MessageModel::rowCount(const QModelIndex &parent) const 
{ 
    return m_data.size(); 
} 

QHash<int, QByteArray> MessageModel::roleNames() const 
{ 
    QHash<int, QByteArray> roles; 
    roles[Id] = "id"; 
    roles[RoomId] = "roomId"; 
    roles[PersonId] = "personId"; 
    roles[PersonEmail] = "personEmail"; 
    roles[Created] = "created"; 
    roles[Text] = "messageText"; 
    return roles; 
} 

QVariant MessageModel::data(const QModelIndex &index, int role) const { 

    if(!index.isValid()) 
     return QVariant(); 

    if(index.row() >= m_data.size()) 
     return QVariant(); 

    switch (role) { 
     case Id: 
      return m_data.at(index.row()).id; 
     case RoomId: 
      return m_data.at(index.row()).roomId; 
     case PersonId: 
      return m_data.at(index.row()).personId; 
     case PersonEmail: 
      return m_data.at(index.row()).personEmail; 
     case Text: 
      return m_data.at(index.row()).text; 
     case Created: 
      return m_data.at(index.row()).created.toString(Qt::ISODateWithMs); 
    } 

    return QVariant(); 
} 

void MessageModel::update_data(QJsonArray new_data) 
{ 
    beginResetModel(); 
    m_data.clear(); 

    foreach (const QJsonValue & val, new_data) { 
     m_data.push_back(LocalData::Message(val.toObject())); 
    } 
    endResetModel(); 
} 

void MessageModel::insert_unique_data(QJsonArray new_data) 
{ 
    QList<LocalData::Message> temp; 
    foreach (const QJsonValue & val, new_data) { 
     auto obj_to_insert = LocalData::Message(val.toObject()); 
     if(!m_data.contains(obj_to_insert)) 
      temp.push_back(obj_to_insert); 
    } 
    int begin = rowCount(); 
    int end = rowCount() + temp.size() - 1; 

    beginInsertRows(QModelIndex(), begin, end); 
    foreach (const LocalData::Message & msg, temp) { 
     m_data.push_back(msg); 
    } 
    endInsertRows(); 
} 

私は両方のソートにQSortFilterProxyModelを使用してメッセージをフィルタリングしたいです。 RoomIdロールに基づいてメッセージを正しくフィルタリングすることができましたが、Createdロールでメッセージを正しく並べ替えるのに問題があります。私が使用している

プロキシモデルは、私はちょうどので、同じようlessThan()関数をオーバーライドしようとした非常に簡単です:

bool SortFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const 
    { 
     QVariant leftData = sourceModel()->data(source_left); 
     QVariant rightData = sourceModel()->data(source_right); 

     if(leftData.type() == QVariant::DateTime) 
     { 
      return leftData.toDateTime() < rightData.toDateTime(); 
     } 
     else { 
      return leftData.toString() < rightData.toString(); 
     } 
    } 

それは単なるQSortFilterProxyModelあるOthervise。私が次の関数を呼び出しています初期化されています :

m_messageModel = new MessageModel; 
m_messageProxyModel = new SortFilterProxyModel; 

m_messageProxyModel->setSourceModel(qobject_cast<QAbstractListModel *>(m_messageModel)); 

m_engine.rootContext()->setContextProperty("messages", m_messageProxyModel); 

m_messageProxyModel.setSortRole(MessageModel::Created); 

m_messageProxyModel.setDynamicSortFilter(true); 

m_messageProxyModel.sort(0, Qt::AscendingOrder); 

m_messageModelm_messageProxyModel、およびm_engineは、すべてのメンバ変数(ポインタ)main()でインスタンス化されたクラスで宣言されています。 m_engineはQQmlApplicationEngineで、変数をqmlに公開します。

すべてのメッセージを含むListViewを含むqmlファイルは次のとおりです。

import QtQuick 2.0 
import QtQuick.Controls 1.4 

Rectangle{ 
    property alias messageView: _messagePanelView 
    Component { 
     id: _messagePanelDelegate 
     Item{ 
      id: _messagePanelDelegateItem; 

      width: root.width * 0.8 
      height: _messagePanelMessageColumn.height 
      Column{ 
       id: _messagePanelMessageColumn; 
       height: children.height; 
       spacing: 20 
       Text{ text: "<b>" + personEmail + "</b> <t />" + created; font.pointSize: 7 + Math.log(root.width) } 
       Text{ text: messageText } 
       Rectangle{ 
        width: parent.width 
        height: 20 
        color: "#FFFFFF" 
       } 
      } 
     } 
    } 
    ScrollView{ 
     anchors.fill: parent 
     ListView { 
      id: _messagePanelView 
      height: root.height - 100 
      model: messages 
      delegate: _messagePanelDelegate 
      interactive: true 
     } 
    } 
} 

ソースモデルには、特定の「チャットルーム」内のすべてのメッセージを尋ねると、現在データが入力されています。ユーザーがボタンを押すと、これに類似した関数が呼び出されます。

void sync_messages_and_filter_based_on_roomId(QString roomId) 
{ 
    m_messageProxyModel->setFilterRole(MessageModel::RoomId); 
    m_messageProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); 
    m_messageProxyModel->setFilterRegExp(QRegExp::escape(roomId)); 

    fetch_messages_in_room_from_server_async(roomId); 
} 

fetch_messages_in_room_from_server_async(roomId);、新しいメッセージのサーバーを要求する機能であるJSON形式の応答ペイロードを受け、QJsonArrayオブジェクトを作成し、別々のワーカースレッドで起こるすべてがvoid MessageModel::insert_unique_data(QJsonArray new_data)を呼び出します。

これが最初に呼び出されると、データは実際には降順(最下部が最古、最近は最上部)でソートされます。しかし、サーバがより多くのデータを提供するようになると、クライアントは新しいメッセージをソースモデルの行としてすべて挿入し、プロキシモデルを昇順に並べ替えます(最新の一番下に、一番上に一番古い)新しいデータが挿入されます。現在のモデルのみ降順にソートされている

は、初めてデータがモデルに挿入されます。しかし、初めてモデルを更新すると、データはソートされなくなり、新しいメッセージは上部の代わりにListViewの下部に表示されます。つまり、プロキシモデルのモデルのソートが停止しています。

Here is an image of the ListView, after inserting a second time. Notice the timestamps This is what happens when I terminate the program, and insert all the messages at once

私が望む結果は昇順でメッセージ(ボタンで最新のが、一番上にある最古の)ソースモデルが更新されるたびに/変更プロキシモデルの並べ替えを持っていることです。 void MessageModel::insert_unique_data(QJsonArray new_data)が呼び出されたときの意味は、新しいメッセージが最後に表示され、最新のメッセージが最後のメッセージであることを意味します。

私の辛抱強く長い記事を読んでいただきありがとうございます。私は、qmlにC++モデルを公開するには多くのステップが必要なので、すべての詳細をカバーしたかっただけです。

+0

ようこそStackOverflow。 +1の書かれた最初の質問をよく書いてください!編集を正当化するのに十分ではなかった。 – derM

答えて

3

あなたのlessThan機能は何もしていないと思います。

QVariant leftData = sourceModel()->data(source_left);を呼び出すときはdataロールを呼び出し、Qt::DisplayRoleロールを使用して、ご使用のモデルに無効​​なQVariantを返します。 if(leftData.type() == QVariant::DateTime)は決してtrueです。

何をすべきことは、明示的にsource_rightためQDateTime leftTimestamp = m_data.at(source_left.row()).created;とタイムスタンプと同じになっています。 あなたifが、その後役に立たない、あなただけの代わりにreturn leftTimestamp < rightTimeStamp;


を行うことができ、あなたがQMLといないのC++からのソートやフィルタリングを行いたい場合、あなたはそのように私のSortFilterProxyModelを使用することができます。

import QtQuick 2.0 
import QtQuick.Controls 1.4 
import SortFilterProxyModel 0.2 

Rectangle{ 
    property alias messageView: _messagePanelView 

    SortFilterProxyModel { 
     id: proxyMessageModel 
     sourceModel: sourceMessageModel // a context property you exposed 
     filters: ValueFilter { 
      roleName: "roomId" 
      value: currentRoomId // a property you could add somewhere 
     } 
     sorters : RoleSorter { 
      roleName: "created" 
     } 
    } 
    Component { 
     id: _messagePanelDelegate 
     Item{ 
      id: _messagePanelDelegateItem; 

      width: root.width * 0.8 
      height: _messagePanelMessageColumn.height 
      Column{ 
       id: _messagePanelMessageColumn; 
       height: children.height; 
       spacing: 20 
       Text{ text: "<b>" + personEmail + "</b> <t />" + created; font.pointSize: 7 + Math.log(root.width) } 
       Text{ text: messageText } 
       Rectangle{ 
        width: parent.width 
        height: 20 
        color: "#FFFFFF" 
       } 
      } 
     } 
    } 
    ScrollView{ 
     anchors.fill: parent 
     ListView { 
      id: _messagePanelView 
      height: root.height - 100 
      model: proxyMessageModel 
      delegate: _messagePanelDelegate 
      interactive: true 
     } 
    } 
} 
+0

うん、あなたは正しい!どうもありがとうございました! QMLベースのプロキシモデルは賢いようだ、私はそれを試してみるよ:) – Hurlevent

2

私はそれを理解しました! SortProxyModel::lessThan()機能で

は、私はデフォルトのロールパラメータを変更せずにQAbstractProxyModel::data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole)機能と呼ばれます。私のlessThan()機能の実装がCreatedの役割と比較されないことを意味します。私はそれだけで通常のQSortProxyModelタイプを使用していた固定方法

、代わりに自分の派生バージョンの。サブクラス化QSortProxyModelは間違いでした。

+0

を実際QSortFilterProxyModel'があなたのケースでは必要ありません 'サブクラス、通常のものを使用すると、正しい解決策である私の答えは、必要がある場合、私は知りません受け入れられたものになる:) – GrecKo

関連する問題