私はチャットクライアントを作っていますが、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_messageModel
、m_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++モデルを公開するには多くのステップが必要なので、すべての詳細をカバーしたかっただけです。
ようこそStackOverflow。 +1の書かれた最初の質問をよく書いてください!編集を正当化するのに十分ではなかった。 – derM