Skip to content

Instantly share code, notes, and snippets.

@danieloneill
Created March 29, 2015 23:54
Show Gist options
  • Save danieloneill/aaed4045bddeb88774a0 to your computer and use it in GitHub Desktop.
Save danieloneill/aaed4045bddeb88774a0 to your computer and use it in GitHub Desktop.
#include "callbackmodel.h"
#include <time.h>
#include <QDebug>
CallbackModel::CallbackModel(QObject *parent):
QAbstractListModel(parent),
m_rowCount(0),
m_cache(true),
m_cacheSize(0),
m_requestDelay(20)
{
m_requestTimer.setSingleShot(true);
connect( &m_requestTimer, SIGNAL(timeout()), this, SLOT(slotRequestData()) );
}
CallbackModel::~CallbackModel()
{
}
void CallbackModel::setRecord( qulonglong idx, QVariant record )
{
m_records[ idx ] = record;
QModelIndex tl = index( idx, 0 );
QModelIndex br = index( idx, 0 );
m_recordsNeeded.removeOne( idx );
emit dataChanged( tl, br );
}
void CallbackModel::setRecords( qulonglong first, QVariant records )
{
if( records.isNull() || !records.isValid() )
return;
QVariantList rl = records.toList();
if( rl.isEmpty() )
return;
int y;
for( y=0; y < rl.count(); y++ )
{
m_records[ y+first ] = rl[ y ];
m_recordsNeeded.removeOne( y+first );
}
QModelIndex tl = index( first, 0 );
QModelIndex br = index( first + rl.count() - 1, 0 );
emit dataChanged( tl, br );
}
void CallbackModel::setRows(qulonglong count)
{
qulonglong oldcount = m_rowCount;
const QModelIndex idx;
if( count == oldcount )
return;
if( count > oldcount )
{
beginInsertRows( idx, oldcount, count-1);
m_rowCount = count;
endInsertRows();
}
else
{
beginRemoveRows( idx, count, oldcount);
m_rowCount = count;
endRemoveRows();
// If we have data past the index, remove it.
for( qulonglong x=count; x < oldcount; x++ )
m_records.remove( x );
}
emit rowCountChanged();
QModelIndex tl = index( oldcount, 0 );
QModelIndex br = index( count, 0 );
emit dataChanged( tl, br );
}
int CallbackModel::rowCount(const QModelIndex & parent) const
{
if( parent.isValid() )
return 0;
return m_rowCount;
}
void CallbackModel::requestData( qulonglong row )
{
m_mutex.lock();
if( m_recordsNeeded.contains(row) )
{
m_mutex.unlock();
return;
}
//qDebug() << "requestData(" << row << ")";
m_recordsNeeded.append( row );
m_requestTimer.start(m_requestDelay);
m_mutex.unlock();
}
QVariant CallbackModel::data(const QModelIndex & index, int role) const
{
Q_UNUSED(role)
QVariantMap invalid;
invalid["valid"] = false;
if( !m_records.contains( index.row() ) )
{
((CallbackModel *)this)->requestData( index.row() );
//qWarning() << "CallbackModel::data for: " << index.row();
return invalid;
}
QVariant r = m_records[ index.row() ];
return r;
/*
if( index.column() >= r.count() )
{
((CallbackModel *)this)->requestData( index.row() );
return invalid;
}
return r[ index.column() ];
*/
}
bool CallbackModel::hasIndex(int row, int column, const QModelIndex & parent) const
{
//qDebug() << "hasIndex(" << row << ", " << column << ")";
if( parent.isValid() )
return false;
if( row > m_rowCount )
return false;
if( row < 0 )
return false;
if( column > 1 )
return false;
return true;
}
void CallbackModel::slotRequestData()
{
//qDebug() << "slotRequestData()!";
if( m_recordsNeeded.length() == 0 )
return;
qSort(m_recordsNeeded);
qulonglong lowest=m_recordsNeeded.first(), highest=m_recordsNeeded.last();
qDebug() << "Requesting records between " << lowest << " and " << highest;
emit recordsRequested( lowest, highest );
}
/*
bool CallbackModel::canFetchMore(const QModelIndex & parent) const
{
if( parent )
return;
if( m_rowCount > m_cacheSize )
return true;
return false;
}
void CallbackModel::fetchMore(const QModelIndex & parent)
{
}
*/
QHash<int, QByteArray> CallbackModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[Qt::UserRole] = "entry";
return roles;
}
#ifndef CALLBACKMODEL_H
#define CALLBACKMODEL_H
#include <QAbstractListModel>
#include <QHash>
#include <QList>
#include <QMutex>
#include <QQuickItem>
#include <QTimer>
#include <QVariant>
class CallbackModel : public QAbstractListModel
{
Q_OBJECT
Q_DISABLE_COPY(CallbackModel)
// Properties:
qulonglong m_rowCount;
bool m_cache;
qulonglong m_cacheSize;
int m_requestDelay;
QMutex m_mutex;
QHash< qulonglong, QVariant > m_records;
QList< qulonglong > m_recordsNeeded;
QTimer m_requestTimer;
public:
CallbackModel(QObject *parent = 0);
~CallbackModel();
Q_PROPERTY( int requestDelay MEMBER m_requestDelay NOTIFY requestDelayChanged )
Q_PROPERTY( qulonglong rows READ rows WRITE setRows NOTIFY rowCountChanged )
Q_PROPERTY( bool cache MEMBER m_cache NOTIFY cacheChanged )
Q_PROPERTY( bool cacheSize MEMBER m_cacheSize NOTIFY cacheSizeChanged )
Q_INVOKABLE static CallbackModel *create() { return new CallbackModel(); }
Q_INVOKABLE qulonglong rows() { return m_rowCount; }
Q_INVOKABLE void setRows(qulonglong count);
Q_INVOKABLE void setRecord( qulonglong index, QVariant record ); // 'record' should be convertable to QVariantList
Q_INVOKABLE void setRecords( qulonglong first, QVariant records ); // 'records' should be convertable to QArray< QVariantList >
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
bool hasIndex(int row, int column, const QModelIndex & parent = QModelIndex()) const;
QHash<int, QByteArray> roleNames() const;
/*
bool canFetchMore(const QModelIndex & parent) const;
void fetchMore(const QModelIndex & parent);
*/
protected:
void requestData( qulonglong row );
protected slots:
void slotRequestData();
signals:
void requestDelayChanged();
void rowCountChanged();
void cacheChanged();
void cacheSizeChanged();
void recordsRequested( qulonglong first, qulonglong last );
};
#endif // CALLBACKMODEL_H
TEMPLATE = lib
TARGET = CallbackModel
QT += qml quick
CONFIG += qt plugin
TARGET = $$qtLibraryTarget($$TARGET)
uri = com.foxmoxie.CallbackModel
# Input
SOURCES += \
callbackmodel_plugin.cpp \
callbackmodel.cpp
HEADERS += \
callbackmodel_plugin.h \
callbackmodel.h
OTHER_FILES = qmldir
!equals(_PRO_FILE_PWD_, $$OUT_PWD) {
copy_qmldir.target = $$OUT_PWD/qmldir
copy_qmldir.depends = $$_PRO_FILE_PWD_/qmldir
copy_qmldir.commands = $(COPY_FILE) \"$$replace(copy_qmldir.depends, /, $$QMAKE_DIR_SEP)\" \"$$replace(copy_qmldir.target, /, $$QMAKE_DIR_SEP)\"
QMAKE_EXTRA_TARGETS += copy_qmldir
PRE_TARGETDEPS += $$copy_qmldir.target
}
qmldir.files = qmldir
unix|win32 {
installPath = $$[QT_INSTALL_QML]/$$replace(uri, \\., /)
qmldir.path = $$installPath
target.path = $$installPath
INSTALLS += target qmldir
}
#include "callbackmodel_plugin.h"
#include "callbackmodel.h"
#include <qqml.h>
void CallbackModelPlugin::registerTypes(const char *uri)
{
// @uri com.foxmoxie.CallbackModel
qmlRegisterType<CallbackModel>(uri, 1, 0, "CallbackModel");
}
#ifndef CALLBACKMODEL_PLUGIN_H
#define CALLBACKMODEL_PLUGIN_H
#include <QQmlExtensionPlugin>
class CallbackModelPlugin : public QQmlExtensionPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
public:
void registerTypes(const char *uri);
};
#endif // CALLBACKMODEL_PLUGIN_H
CallbackModel {
id: gridModel
property string catToken: ''
property string prodToken: ''
property int catCount: 0
property int prodCount: 0
requestDelay: 50
onRecordsRequested: {
// Might be categories, might be products:
if( first < catCount )
{
socket.transmit( { 'module': 'products', 'function': 'query', 'action':'fetch', 'query':catToken, 'start':first, 'end':last } );
} else // Subtract category count prefixing product items.
socket.transmit( { 'module': 'products', 'function': 'query', 'action':'fetch', 'query':prodToken, 'start':first-catCount, 'end':last-catCount } );
}
function clear()
{
// If there's a token, inform the server that it can discard it now:
if( catToken.length > 0 )
socket.transmit( { 'module': 'products', 'function': 'query', 'action':'discard', 'query':catToken } );
if( prodToken.length > 0 )
socket.transmit( { 'module': 'products', 'function': 'query', 'action':'discard', 'query':prodToken } );
prodToken = '';
catToken = '';
catCount = 0;
prodCount = 0;
gridModel.rows = 0;
}
}
function requestCategory()
{
gridModel.clear();
socket.transmit( { 'module': 'products', 'function': 'getCategory', 'id': categoryId } );
socket.transmit( { 'module': 'products', 'function': 'getCategoryChildren', 'id': categoryId } );
socket.transmit( { 'module': 'products', 'function': 'getCategoryProducts', 'id': categoryId, 'deferred':true, 'preroll':10 } );
}
function handlePacket(packet)
{
if( packet['module'] !== 'products' )
return false;
if( packet['id'] !== categoryId )
return false;
if( packet['function'] == 'getCategoryChildren' )
{
if( !packet['categories'] )
return true;
if( packet['query'] )
gridModel.catToken = packet['query'];
if( packet['total'] )
gridModel.catCount = packet['total'];
else
gridModel.catCount = packet['categories'].length;
var firstIndex = 0;
if( packet['categories'] && packet['categories'].length && packet['categories'].length > 0 )
{
if( packet['categories'][0]['index'] )
firstIndex = packet['categories'][0]['index'];
}
var entries = [];
for( var x=0; x < packet['categories'].length; x++ )
{
var entry = { 'type': 'category', 'entry': packet['categories'][x] };
entries.push( entry );
}
if( entries && entries.length > 0 )
gridModel.setRecords( firstIndex, entries );
gridModel.rows = gridModel.catCount + gridModel.prodCount;
}
else if( packet['function'] == 'getCategoryProducts' )
{
if( !packet['products'] )
return true;
gridModel.prodToken = packet['query'];
if( packet['total'] )
gridModel.prodCount = packet['total'];
else
gridModel.prodCount = packet['products'].length;
var firstIndex = 0;
if( packet['products'] && packet['products'].length && packet['products'].length > 0 )
firstIndex = packet['products'][0]['index'];
var entries = [];
for( var x=0; x < packet['products'].length; x++ )
{
var entry = { 'type': 'product', 'entry': packet['products'][x] };
entries.push( entry );
}
// Offset from categories kthx:
firstIndex += gridModel.catCount;
console.log("Adding "+entries.length+" products...");
if( entries && entries.length > 0 )
gridModel.setRecords( firstIndex, entries );
gridModel.rows = gridModel.catCount + gridModel.prodCount;
console.log( "Category count: "+gridModel.catCount+" / Product count: "+gridModel.prodCount);
}
else if( packet['function'] == 'getCategory' )
{
flipchart.parentId = packet['category']['parentId'];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment